From 698594087c32d982cb5b8558bb8ba01984882a3b Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 23 Mar 2022 01:53:43 +0000 Subject: [PATCH 01/16] Amend editor config for black Also restructure, separate js and move to tabs Add md spec --- .editorconfig | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/.editorconfig b/.editorconfig index c6a6fff7..3a7dfa1d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,15 +2,25 @@ root = true -[*.{js,py,html}] +[*] charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true insert_final_newline = true -# end_of_line = lf [*.py] -indent_style = space -indent_size = 4 +profile = black +# > Handled by Black +# indent_style = space +# indent_size = 4 -[*.{js,html}] +[*.md] +trim_trailing_whitespace = false + +[*.html] indent_style = space indent_size = 2 + +[*.js] +indent_style = tab +indent_size = 4 From dfd32204827d919c8708d3983aa1deba572a4686 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 23 Mar 2022 01:57:46 +0000 Subject: [PATCH 02/16] Add black cicd testing --- .gitlab-ci.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6d0a4212..708bb257 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ stages: -- test +- lint - prod-deployment - dev-deployment @@ -7,8 +7,20 @@ variables: DOCKER_HOST: tcp://docker:2376 DOCKER_TLS_CERTDIR: "/certs" +black: + stage: lint + image: registry.gitlab.com/pipeline-components/black:latest + tags: + - 'docker' + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS' + when: never + script: + - black --check --verbose -- . + pylint: - stage: test + stage: lint image: python:3.7-slim tags: - 'docker' From 416543d33e5ff32825892fe7761ed76860fdde00 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 23 Mar 2022 02:01:41 +0000 Subject: [PATCH 03/16] Add extra linting to validate json files --- .gitlab-ci.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 708bb257..a8d456e2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,6 +7,20 @@ variables: DOCKER_HOST: tcp://docker:2376 DOCKER_TLS_CERTDIR: "/certs" +jsonlint: + stage: lint + image: registry.gitlab.com/pipeline-components/jsonlint:latest + tags: + - 'docker' + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS' + when: never + script: + - | + find . -not -path './.git/*' -name '*.json' -type f -print0 | + parallel --will-cite -k -0 -n1 jsonlint -q + black: stage: lint image: registry.gitlab.com/pipeline-components/black:latest From fc0adcd3cb30756d2943ce90b9b2133f84e2098c Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 23 Mar 2022 02:05:02 +0000 Subject: [PATCH 04/16] Add extra yaml lint yamllint does not only check for syntax validity, but for weirdnesses like key repetition and cosmetic problems such as lines length, trailing spaces, indentation, etc. --- .gitlab-ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a8d456e2..54838ba7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,6 +7,12 @@ variables: DOCKER_HOST: tcp://docker:2376 DOCKER_TLS_CERTDIR: "/certs" +yamllint: + stage: lint + image: registry.gitlab.com/pipeline-components/yamllint:latest + script: + - yamllint . + jsonlint: stage: lint image: registry.gitlab.com/pipeline-components/jsonlint:latest From f5ace96fe669bff1bba7a1f5e3e4a909ef6a2a36 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 23 Mar 2022 02:12:18 +0000 Subject: [PATCH 05/16] Add missing runner tags and triggers for ymllint --- .gitlab-ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 54838ba7..72bac27b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,6 +10,12 @@ variables: yamllint: stage: lint image: registry.gitlab.com/pipeline-components/yamllint:latest + tags: + - 'docker' + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS' + when: never script: - yamllint . From 6664ff276b6c84e015907c5ef433cdab6883b70c Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 23 Mar 2022 02:32:41 +0000 Subject: [PATCH 06/16] Lint corrections to gitlab-ci yml --- .gitlab-ci.yml | 105 ++++++++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 50 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 72bac27b..cf3229fc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,8 @@ +# Crafty Controller 4.0 - Lint & Build Pipes +# [Maintainer: Zedifus(https://gitlab.com/Zedifus)] +################################################### +# yamllint disable rule:line-length +--- stages: - lint - prod-deployment @@ -162,81 +167,81 @@ docker-build-prod: win-dev-build: stage: dev-deployment tags: - - win64 + - win64 cache: paths: - .venv/ rules: - - if: "$CI_COMMIT_BRANCH == 'dev'" + - if: "$CI_COMMIT_BRANCH == 'dev'" environment: name: development script: - - | - $ErrorActionPreference = "Stop" - py -m venv .venv - .venv\Scripts\activate.ps1 - pip install pyinstaller - pip install -r requirements.txt - - pyinstaller -F main.py - --distpath . - --icon app\frontend\static\assets\images\Crafty_4-0_Logo_square.ico - --name "crafty_commander" - --paths .venv\Lib\site-packages - --hidden-import cryptography - --hidden-import cffi - --hidden-import apscheduler - --collect-all tzlocal - --collect-all tzdata - --collect-all pytz - --collect-all six + - | + $ErrorActionPreference = "Stop" + py -m venv .venv + .venv\Scripts\activate.ps1 + pip install pyinstaller + pip install -r requirements.txt + - pyinstaller -F main.py + --distpath . + --icon app\frontend\static\assets\images\Crafty_4-0_Logo_square.ico + --name "crafty_commander" + --paths .venv\Lib\site-packages + --hidden-import cryptography + --hidden-import cffi + --hidden-import apscheduler + --collect-all tzlocal + --collect-all tzdata + --collect-all pytz + --collect-all six artifacts: name: "crafty-${CI_RUNNER_TAGS}-${CI_COMMIT_BRANCH}_${CI_COMMIT_SHORT_SHA}" paths: - - app\ - - .\crafty_commander.exe + - app\ + - .\crafty_commander.exe exclude: - - app\classes\**\* - # Download latest: - # | https://gitlab.com/crafty-controller/crafty-commander/-/jobs/artifacts/dev/download?job=win-dev-build + - app\classes\**\* + # Download latest: + # | https://gitlab.com/crafty-controller/crafty-commander/-/jobs/artifacts/dev/download?job=win-dev-build win-prod-build: stage: prod-deployment tags: - - win64 + - win64 cache: paths: - .venv/ rules: - - if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH" + - if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH" environment: name: production script: - - | - $ErrorActionPreference = "Stop" - py -m venv .venv - .venv\Scripts\activate.ps1 - pip install pyinstaller - pip install -r requirements.txt - - pyinstaller -F main.py - --distpath . - --icon app\frontend\static\assets\images\Crafty_4-0_Logo_square.ico - --name "crafty_commander" - --paths .venv\Lib\site-packages - --hidden-import cryptography - --hidden-import cffi - --hidden-import apscheduler - --collect-all tzlocal - --collect-all tzdata - --collect-all pytz - --collect-all six + - | + $ErrorActionPreference = "Stop" + py -m venv .venv + .venv\Scripts\activate.ps1 + pip install pyinstaller + pip install -r requirements.txt + - pyinstaller -F main.py + --distpath . + --icon app\frontend\static\assets\images\Crafty_4-0_Logo_square.ico + --name "crafty_commander" + --paths .venv\Lib\site-packages + --hidden-import cryptography + --hidden-import cffi + --hidden-import apscheduler + --collect-all tzlocal + --collect-all tzdata + --collect-all pytz + --collect-all six artifacts: name: "crafty-${CI_RUNNER_TAGS}-${CI_COMMIT_BRANCH}_${CI_COMMIT_SHORT_SHA}" paths: - - app\ - - .\crafty_commander.exe + - app\ + - .\crafty_commander.exe exclude: - - app\classes\**\* - # Download latest: - # | https://gitlab.com/crafty-controller/crafty-commander/-/jobs/artifacts/master/download?job=win-prod-build + - app\classes\**\* + # Download latest: + # | https://gitlab.com/crafty-controller/crafty-commander/-/jobs/artifacts/master/download?job=win-prod-build From 932d81fe8131b386d72caf0c9f9c63fa305b195f Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 23 Mar 2022 02:37:52 +0000 Subject: [PATCH 07/16] Correct yml for compose file and example for lint --- docker-compose.yml.example | 11 ++++++----- docker/docker-compose.yml | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/docker-compose.yml.example b/docker-compose.yml.example index 4de61fd7..38bf54c3 100644 --- a/docker-compose.yml.example +++ b/docker-compose.yml.example @@ -1,3 +1,4 @@ +--- version: '3' services: @@ -7,11 +8,11 @@ services: environment: - TZ=Etc/UTC ports: - - "8000:8000" # HTTP - - "8443:8443" # HTTPS - - "8123:8123" # DYNMAP - - "19132:19132/udp" # BEDROCK - - "25500-25600:25500-25600" # MC SERV PORT RANGE + - "8000:8000" # HTTP + - "8443:8443" # HTTPS + - "8123:8123" # DYNMAP + - "19132:19132/udp" # BEDROCK + - "25500-25600:25500-25600" # MC SERV PORT RANGE volumes: - ./docker/backups:/commander/backups - ./docker/logs:/commander/logs diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index cefacb35..4342880c 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,3 +1,4 @@ +--- version: '3' services: @@ -7,11 +8,11 @@ services: environment: - TZ=Etc/UTC ports: - - "8000:8000" # HTTP - - "8443:8443" # HTTPS - - "8123:8123" # DYNMAP - - "19132:19132/udp" # BEDROCK - - "25500-25600:25500-25600" # MC SERV PORT RANGE + - "8000:8000" # HTTP + - "8443:8443" # HTTPS + - "8123:8123" # DYNMAP + - "19132:19132/udp" # BEDROCK + - "25500-25600:25500-25600" # MC SERV PORT RANGE volumes: - ./backups:/commander/backups - ./logs:/commander/logs From 69b547a5534fb7d2ae2ad30d5c7b816757d053ad Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 23 Mar 2022 02:41:19 +0000 Subject: [PATCH 08/16] Furthor minor yml corrections --- .gitlab-ci.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cf3229fc..bd9d32ed 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,9 +4,9 @@ # yamllint disable rule:line-length --- stages: -- lint -- prod-deployment -- dev-deployment + - lint + - prod-deployment + - dev-deployment variables: DOCKER_HOST: tcp://docker:2376 @@ -195,6 +195,8 @@ win-dev-build: --collect-all pytz --collect-all six + # Download latest: + # | https://gitlab.com/crafty-controller/crafty-commander/-/jobs/artifacts/dev/download?job=win-dev-build artifacts: name: "crafty-${CI_RUNNER_TAGS}-${CI_COMMIT_BRANCH}_${CI_COMMIT_SHORT_SHA}" paths: @@ -202,8 +204,7 @@ win-dev-build: - .\crafty_commander.exe exclude: - app\classes\**\* - # Download latest: - # | https://gitlab.com/crafty-controller/crafty-commander/-/jobs/artifacts/dev/download?job=win-dev-build + win-prod-build: stage: prod-deployment @@ -236,6 +237,8 @@ win-prod-build: --collect-all pytz --collect-all six + # Download latest: + # | https://gitlab.com/crafty-controller/crafty-commander/-/jobs/artifacts/master/download?job=win-prod-build artifacts: name: "crafty-${CI_RUNNER_TAGS}-${CI_COMMIT_BRANCH}_${CI_COMMIT_SHORT_SHA}" paths: @@ -243,5 +246,4 @@ win-prod-build: - .\crafty_commander.exe exclude: - app\classes\**\* - # Download latest: - # | https://gitlab.com/crafty-controller/crafty-commander/-/jobs/artifacts/master/download?job=win-prod-build + From 9d8ed37341fb6ecbffef62aa29f721b7e37ce9bb Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 23 Mar 2022 02:48:55 +0000 Subject: [PATCH 09/16] Last lint correction on gitlab-ci file --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bd9d32ed..07faa88b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -246,4 +246,3 @@ win-prod-build: - .\crafty_commander.exe exclude: - app\classes\**\* - From e06fe8a52a55dda5698e5a1444db3c4409f78002 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 23 Mar 2022 02:50:12 +0000 Subject: [PATCH 10/16] =?UTF-8?q?=E2=AC=9BBlack=20codebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply black formatting to codebase --- .../controllers/crafty_perms_controller.py | 31 +- .../controllers/management_controller.py | 77 +- app/classes/controllers/roles_controller.py | 27 +- .../controllers/server_perms_controller.py | 39 +- app/classes/controllers/servers_controller.py | 103 +- app/classes/controllers/users_controller.py | 90 +- app/classes/minecraft/bedrock_ping.py | 69 +- app/classes/minecraft/mc_ping.py | 85 +- app/classes/minecraft/server_props.py | 20 +- app/classes/minecraft/serverjars.py | 65 +- app/classes/minecraft/stats.py | 175 +- app/classes/models/crafty_permissions.py | 121 +- app/classes/models/management.py | 264 ++- app/classes/models/roles.py | 37 +- app/classes/models/server_permissions.py | 141 +- app/classes/models/servers.py | 189 +- app/classes/models/users.py | 228 ++- app/classes/shared/authentication.py | 27 +- app/classes/shared/command.py | 40 +- app/classes/shared/console.py | 10 +- app/classes/shared/exceptions.py | 2 + app/classes/shared/file_helpers.py | 48 +- app/classes/shared/helpers.py | 365 ++-- app/classes/shared/import3.py | 65 +- app/classes/shared/installer.py | 12 +- app/classes/shared/main_controller.py | 485 +++-- app/classes/shared/main_models.py | 30 +- app/classes/shared/migration.py | 156 +- app/classes/shared/permission_helper.py | 13 +- app/classes/shared/server.py | 1167 ++++++----- app/classes/shared/tasks.py | 548 +++--- app/classes/shared/translation.py | 41 +- app/classes/web/ajax_handler.py | 410 ++-- app/classes/web/api_handler.py | 54 +- app/classes/web/base_handler.py | 36 +- app/classes/web/default_handler.py | 3 +- app/classes/web/file_handler.py | 442 +++-- app/classes/web/http_handler.py | 17 +- app/classes/web/http_handler_page.py | 22 +- app/classes/web/panel_handler.py | 1748 ++++++++++------- app/classes/web/public_handler.py | 124 +- app/classes/web/server_handler.py | 421 ++-- app/classes/web/static_handler.py | 13 +- app/classes/web/status_handler.py | 58 +- app/classes/web/tornado_handler.py | 109 +- app/classes/web/upload_handler.py | 84 +- app/classes/web/websocket_handler.py | 62 +- app/classes/web/websocket_helper.py | 28 +- app/migrations/20210813111015_init.py | 77 +- app/migrations/20210819155737_permissions.py | 17 +- .../20210822092240_crafty_permissions.py | 12 +- .../20210822101530_delete_User_Servers.py | 15 +- .../20210824205501_permissions_limits_1.py | 26 +- .../20210915205501_backup_schedule.py | 9 +- app/migrations/20210915205501_crash.py | 6 +- app/migrations/20210915205501_cron_task.py | 6 +- app/migrations/20210915205501_first_run_.py | 6 +- .../20210915205501_one_time_task.py | 6 +- app/migrations/20210915205501_server_type.py | 6 +- app/migrations/20210915205501_user_email.py | 6 +- .../20210915205501_users_log_path.py | 6 +- .../20210915205501_waiting_start_1.py | 8 +- app/migrations/20210929205501_user_lang.py | 6 +- app/migrations/20211120221511_api_keys.py | 12 +- .../20211121233959_multi_api_keys.py | 12 +- app/migrations/20220223_schedule_reaction.py | 10 +- app/migrations/20220226_server_order.py | 6 +- .../20220227162446_backup_options.py | 6 +- app/migrations/20220302_compress_backups.py | 6 +- app/migrations/20220303_downloading.py | 6 +- app/migrations/20220312_support_log_status.py | 6 +- main.py | 70 +- 72 files changed, 5296 insertions(+), 3451 deletions(-) diff --git a/app/classes/controllers/crafty_perms_controller.py b/app/classes/controllers/crafty_perms_controller.py index 75cf2601..4f50d0bb 100644 --- a/app/classes/controllers/crafty_perms_controller.py +++ b/app/classes/controllers/crafty_perms_controller.py @@ -1,12 +1,15 @@ import logging -from app.classes.models.crafty_permissions import crafty_permissions, Enum_Permissions_Crafty +from app.classes.models.crafty_permissions import ( + crafty_permissions, + Enum_Permissions_Crafty, +) from app.classes.models.users import ApiKeys logger = logging.getLogger(__name__) -class Crafty_Perms_Controller: +class Crafty_Perms_Controller: @staticmethod def list_defined_crafty_permissions(): permissions_list = crafty_permissions.get_permissions_list() @@ -18,23 +21,29 @@ class Crafty_Perms_Controller: return permissions_mask @staticmethod - def set_permission(permission_mask, permission_tested: Enum_Permissions_Crafty, value): - return crafty_permissions.set_permission(permission_mask, permission_tested, value) + def set_permission( + permission_mask, permission_tested: Enum_Permissions_Crafty, value + ): + return crafty_permissions.set_permission( + permission_mask, permission_tested, value + ) @staticmethod def can_create_server(user_id): - return crafty_permissions.can_add_in_crafty(user_id, Enum_Permissions_Crafty.Server_Creation) + return crafty_permissions.can_add_in_crafty( + user_id, Enum_Permissions_Crafty.Server_Creation + ) @staticmethod - def can_add_user(): # Add back argument 'user_id' when you work on this - #TODO: Complete if we need a User Addition limit - #return crafty_permissions.can_add_in_crafty(user_id, Enum_Permissions_Crafty.User_Config) + def can_add_user(): # Add back argument 'user_id' when you work on this + # TODO: Complete if we need a User Addition limit + # return crafty_permissions.can_add_in_crafty(user_id, Enum_Permissions_Crafty.User_Config) return True @staticmethod - def can_add_role(): # Add back argument 'user_id' when you work on this - #TODO: Complete if we need a Role Addition limit - #return crafty_permissions.can_add_in_crafty(user_id, Enum_Permissions_Crafty.Roles_Config) + def can_add_role(): # Add back argument 'user_id' when you work on this + # TODO: Complete if we need a Role Addition limit + # return crafty_permissions.can_add_in_crafty(user_id, Enum_Permissions_Crafty.Roles_Config) return True @staticmethod diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py index 59419ca0..f2d62d75 100644 --- a/app/classes/controllers/management_controller.py +++ b/app/classes/controllers/management_controller.py @@ -5,18 +5,19 @@ from app.classes.models.servers import servers_helper logger = logging.getLogger(__name__) + class Management_Controller: - #************************************************************************************************ + # ************************************************************************************************ # Host_Stats Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_latest_hosts_stats(): return management_helper.get_latest_hosts_stats() - #************************************************************************************************ + # ************************************************************************************************ # Commands Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_unactioned_commands(): return management_helper.get_unactioned_commands() @@ -26,43 +27,61 @@ class Management_Controller: server_name = servers_helper.get_server_friendly_name(server_id) # Example: Admin issued command start_server for server Survival - management_helper.add_to_audit_log(user_id, f"issued command {command} for server {server_name}", server_id, remote_ip) + management_helper.add_to_audit_log( + user_id, + f"issued command {command} for server {server_name}", + server_id, + remote_ip, + ) management_helper.add_command(server_id, user_id, remote_ip, command) @staticmethod def mark_command_complete(command_id=None): return management_helper.mark_command_complete(command_id) - #************************************************************************************************ + # ************************************************************************************************ # Audit_Log Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_actity_log(): return management_helper.get_actity_log() @staticmethod def add_to_audit_log(user_id, log_msg, server_id=None, source_ip=None): - return management_helper.add_to_audit_log(user_id, log_msg, server_id, source_ip) + return management_helper.add_to_audit_log( + user_id, log_msg, server_id, source_ip + ) @staticmethod def add_to_audit_log_raw(user_name, user_id, server_id, log_msg, source_ip): - return management_helper.add_to_audit_log_raw(user_name, user_id, server_id, log_msg, source_ip) + return management_helper.add_to_audit_log_raw( + user_name, user_id, server_id, log_msg, source_ip + ) - #************************************************************************************************ + # ************************************************************************************************ # Schedules Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod - def create_scheduled_task(server_id, action, interval, interval_type, start_time, command, comment=None, enabled=True): + def create_scheduled_task( + server_id, + action, + interval, + interval_type, + start_time, + command, + comment=None, + enabled=True, + ): return management_helper.create_scheduled_task( - server_id, - action, - interval, - interval_type, - start_time, - command, - comment, - enabled - ) + server_id, + action, + interval, + interval_type, + start_time, + command, + comment, + enabled, + ) @staticmethod def delete_scheduled_task(schedule_id): @@ -96,16 +115,24 @@ class Management_Controller: def get_schedules_enabled(): return management_helper.get_schedules_enabled() - #************************************************************************************************ + # ************************************************************************************************ # Backups Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_backup_config(server_id): return management_helper.get_backup_config(server_id) @staticmethod - def set_backup_config(server_id: int, backup_path: str = None, max_backups: int = None, excluded_dirs: list = None, compress: bool = False,): - return management_helper.set_backup_config(server_id, backup_path, max_backups, excluded_dirs, compress) + def set_backup_config( + server_id: int, + backup_path: str = None, + max_backups: int = None, + excluded_dirs: list = None, + compress: bool = False, + ): + return management_helper.set_backup_config( + server_id, backup_path, max_backups, excluded_dirs, compress + ) @staticmethod def get_excluded_backup_dirs(server_id: int): diff --git a/app/classes/controllers/roles_controller.py b/app/classes/controllers/roles_controller.py index bded6e23..224995df 100644 --- a/app/classes/controllers/roles_controller.py +++ b/app/classes/controllers/roles_controller.py @@ -7,11 +7,11 @@ from app.classes.shared.helpers import helper logger = logging.getLogger(__name__) -class Roles_Controller: +class Roles_Controller: @staticmethod def get_all_roles(): - return roles_helper.get_all_roles() + return roles_helper.get_all_roles() @staticmethod def get_roleid_by_name(role_name): @@ -21,9 +21,8 @@ class Roles_Controller: def get_role(role_id): return roles_helper.get_role(role_id) - @staticmethod - def update_role(role_id: str, role_data = None, permissions_mask: str = "00000000"): + def update_role(role_id: str, role_data=None, permissions_mask: str = "00000000"): if role_data is None: role_data = {} base_data = Roles_Controller.get_role_with_servers(role_id) @@ -34,15 +33,17 @@ class Roles_Controller: if key == "role_id": continue elif key == "servers": - added_servers = role_data['servers'].difference(base_data['servers']) - removed_servers = base_data['servers'].difference(role_data['servers']) + added_servers = role_data["servers"].difference(base_data["servers"]) + removed_servers = base_data["servers"].difference(role_data["servers"]) elif base_data[key] != role_data[key]: up_data[key] = role_data[key] - up_data['last_update'] = helper.get_time_as_string() - logger.debug(f"role: {role_data} +server:{added_servers} -server{removed_servers}") + up_data["last_update"] = helper.get_time_as_string() + logger.debug( + f"role: {role_data} +server:{added_servers} -server{removed_servers}" + ) for server in added_servers: server_permissions.get_or_create(role_id, server, permissions_mask) - for server in base_data['servers']: + for server in base_data["servers"]: server_permissions.update_role_permission(role_id, server, permissions_mask) # TODO: This is horribly inefficient and we should be using bulk queries but im going for functionality at this point server_permissions.delete_roles_permissions(role_id, removed_servers) @@ -56,7 +57,7 @@ class Roles_Controller: @staticmethod def remove_role(role_id): role_data = Roles_Controller.get_role_with_servers(role_id) - server_permissions.delete_roles_permissions(role_id, role_data['servers']) + server_permissions.delete_roles_permissions(role_id, role_data["servers"]) users_helper.remove_roles_from_role_id(role_id) return roles_helper.remove_role(role_id) @@ -74,9 +75,9 @@ class Roles_Controller: servers = set() for s in servers_query: servers.add(s.server_id.server_id) - role['servers'] = servers - #logger.debug("role: ({}) {}".format(role_id, role)) + role["servers"] = servers + # logger.debug("role: ({}) {}".format(role_id, role)) return role else: - #logger.debug("role: ({}) {}".format(role_id, {})) + # logger.debug("role: ({}) {}".format(role_id, {})) return {} diff --git a/app/classes/controllers/server_perms_controller.py b/app/classes/controllers/server_perms_controller.py index bd0ae36d..931f4f1e 100644 --- a/app/classes/controllers/server_perms_controller.py +++ b/app/classes/controllers/server_perms_controller.py @@ -1,6 +1,9 @@ import logging -from app.classes.models.server_permissions import server_permissions, Enum_Permissions_Server +from app.classes.models.server_permissions import ( + server_permissions, + Enum_Permissions_Server, +) from app.classes.models.users import users_helper, ApiKeys from app.classes.models.roles import roles_helper from app.classes.models.servers import servers_helper @@ -8,8 +11,8 @@ from app.classes.shared.main_models import db_helper logger = logging.getLogger(__name__) -class Server_Perms_Controller: +class Server_Perms_Controller: @staticmethod def get_server_user_list(server_id): return server_permissions.get_server_user_list(server_id) @@ -42,20 +45,28 @@ class Server_Perms_Controller: role_list = server_permissions.get_server_roles(old_server_id) for role in role_list: server_permissions.add_role_server( - new_server_id, role.role_id, - server_permissions.get_permissions_mask(int(role.role_id), int(old_server_id))) - #server_permissions.add_role_server(new_server_id, role.role_id, '00001000') + new_server_id, + role.role_id, + server_permissions.get_permissions_mask( + int(role.role_id), int(old_server_id) + ), + ) + # server_permissions.add_role_server(new_server_id, role.role_id, '00001000') - #************************************************************************************************ + # ************************************************************************************************ # Servers Permissions Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_permissions_mask(role_id, server_id): return server_permissions.get_permissions_mask(role_id, server_id) @staticmethod - def set_permission(permission_mask, permission_tested: Enum_Permissions_Server, value): - return server_permissions.set_permission(permission_mask, permission_tested, value) + def set_permission( + permission_mask, permission_tested: Enum_Permissions_Server, value + ): + return server_permissions.set_permission( + permission_mask, permission_tested, value + ) @staticmethod def get_role_permissions_list(role_id): @@ -86,7 +97,9 @@ class Server_Perms_Controller: roles_list.append(roles_helper.get_role(u.role_id)) for r in roles_list: - role_test = server_permissions.get_role_servers_from_role_id(r.get('role_id')) + role_test = server_permissions.get_role_servers_from_role_id( + r.get("role_id") + ) for t in role_test: role_server.append(t) @@ -94,6 +107,8 @@ class Server_Perms_Controller: authorized_servers.append(servers_helper.get_server_data_by_id(s.server_id)) for s in authorized_servers: - latest = servers_helper.get_latest_server_stats(s.get('server_id')) - server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)[0]}) + latest = servers_helper.get_latest_server_stats(s.get("server_id")) + server_data.append( + {"server_data": s, "stats": db_helper.return_rows(latest)[0]} + ) return server_data diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 4a596428..cfec8375 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -5,17 +5,21 @@ import json from app.classes.controllers.roles_controller import Roles_Controller from app.classes.models.servers import servers_helper from app.classes.models.users import users_helper, ApiKeys -from app.classes.models.server_permissions import server_permissions, Enum_Permissions_Server +from app.classes.models.server_permissions import ( + server_permissions, + Enum_Permissions_Server, +) from app.classes.shared.helpers import helper from app.classes.shared.main_models import db_helper logger = logging.getLogger(__name__) + class Servers_Controller: - #************************************************************************************************ + # ************************************************************************************************ # Generic Servers Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def create_server( name: str, @@ -27,7 +31,8 @@ class Servers_Controller: server_log_file: str, server_stop: str, server_type: str, - server_port=25565): + server_port=25565, + ): return servers_helper.create_server( name, server_uuid, @@ -38,7 +43,8 @@ class Servers_Controller: server_log_file, server_stop, server_type, - server_port) + server_port, + ) @staticmethod def get_server_obj(server_id): @@ -66,8 +72,8 @@ class Servers_Controller: for role in roles_list: role_id = role.role_id role_data = Roles_Controller.get_role_with_servers(role_id) - role_data['servers'] = {server_id} - server_permissions.delete_roles_permissions(role_id, role_data['servers']) + role_data["servers"] = {server_id} + server_permissions.delete_roles_permissions(role_id, role_data["servers"]) server_permissions.remove_roles_of_server(server_id) servers_helper.remove_server(server_id) @@ -75,9 +81,9 @@ class Servers_Controller: def get_server_data_by_id(server_id): return servers_helper.get_server_data_by_id(server_id) - #************************************************************************************************ + # ************************************************************************************************ # Servers Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_all_defined_servers(): return servers_helper.get_all_defined_servers() @@ -100,17 +106,26 @@ class Servers_Controller: @staticmethod def get_authorized_servers_stats_api_key(api_key: ApiKeys): server_data = [] - authorized_servers = Servers_Controller.get_authorized_servers(api_key.user.user_id) + authorized_servers = Servers_Controller.get_authorized_servers( + api_key.user.user_id + ) for s in authorized_servers: - latest = servers_helper.get_latest_server_stats(s.get('server_id')) - key_permissions = server_permissions.get_api_key_permissions_list(api_key, s.get('server_id')) + latest = servers_helper.get_latest_server_stats(s.get("server_id")) + key_permissions = server_permissions.get_api_key_permissions_list( + api_key, s.get("server_id") + ) if Enum_Permissions_Server.Commands in key_permissions: user_command_permission = True else: user_command_permission = False - server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)[0], - "user_command_permission": user_command_permission}) + server_data.append( + { + "server_data": s, + "stats": db_helper.return_rows(latest)[0], + "user_command_permission": user_command_permission, + } + ) return server_data @staticmethod @@ -119,18 +134,22 @@ class Servers_Controller: authorized_servers = Servers_Controller.get_authorized_servers(user_id) for s in authorized_servers: - latest = servers_helper.get_latest_server_stats(s.get('server_id')) + latest = servers_helper.get_latest_server_stats(s.get("server_id")) # TODO - user_permissions = server_permissions.get_user_id_permissions_list(user_id, s.get('server_id')) + user_permissions = server_permissions.get_user_id_permissions_list( + user_id, s.get("server_id") + ) if Enum_Permissions_Server.Commands in user_permissions: user_command_permission = True else: user_command_permission = False - server_data.append({ - 'server_data': s, - 'stats': db_helper.return_rows(latest)[0], - 'user_command_permission': user_command_permission - }) + server_data.append( + { + "server_data": s, + "stats": db_helper.return_rows(latest)[0], + "user_command_permission": user_command_permission, + } + ) return server_data @@ -138,9 +157,9 @@ class Servers_Controller: def get_server_friendly_name(server_id): return servers_helper.get_server_friendly_name(server_id) - #************************************************************************************************ + # ************************************************************************************************ # Servers_Stats Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_server_stats_by_id(server_id): return servers_helper.get_server_stats_by_id(server_id) @@ -157,7 +176,9 @@ class Servers_Controller: def server_id_authorized(server_id_a, user_id): user_roles = users_helper.user_role_query(user_id) for role in user_roles: - for server_id_b in server_permissions.get_role_servers_from_role_id(role.role_id): + for server_id_b in server_permissions.get_role_servers_from_role_id( + role.role_id + ): if str(server_id_a) == str(server_id_b.server_id): return True return False @@ -197,21 +218,23 @@ class Servers_Controller: def get_update_status(server_id): return servers_helper.get_update_status(server_id) - #************************************************************************************************ + # ************************************************************************************************ # Servers Helpers Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_banned_players(server_id): stats = servers_helper.get_server_stats_by_id(server_id) - server_path = stats['server_id']['path'] - path = os.path.join(server_path, 'banned-players.json') + server_path = stats["server_id"]["path"] + path = os.path.join(server_path, "banned-players.json") try: - with open(helper.get_os_understandable_path(path), encoding='utf-8') as file: + with open( + helper.get_os_understandable_path(path), encoding="utf-8" + ) as file: content = file.read() file.close() except Exception as ex: - print (ex) + print(ex) return None return json.loads(content) @@ -219,18 +242,20 @@ class Servers_Controller: def check_for_old_logs(self): servers = servers_helper.get_all_defined_servers() for server in servers: - logs_path = os.path.split(server['log_path'])[0] - latest_log_file = os.path.split(server['log_path'])[1] - logs_delete_after = int(server['logs_delete_after']) + logs_path = os.path.split(server["log_path"])[0] + latest_log_file = os.path.split(server["log_path"])[1] + logs_delete_after = int(server["logs_delete_after"]) if logs_delete_after == 0: continue - log_files = list(filter( - lambda val: val != latest_log_file, - os.listdir(logs_path) - )) + log_files = list( + filter(lambda val: val != latest_log_file, os.listdir(logs_path)) + ) for log_file in log_files: log_file_path = os.path.join(logs_path, log_file) - if helper.check_file_exists(log_file_path) and \ - helper.is_file_older_than_x_days(log_file_path, logs_delete_after): + if helper.check_file_exists( + log_file_path + ) and helper.is_file_older_than_x_days( + log_file_path, logs_delete_after + ): os.remove(log_file_path) diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py index bd51fd8f..af590046 100644 --- a/app/classes/controllers/users_controller.py +++ b/app/classes/controllers/users_controller.py @@ -2,17 +2,21 @@ import logging from typing import Optional from app.classes.models.users import users_helper -from app.classes.models.crafty_permissions import crafty_permissions, Enum_Permissions_Crafty +from app.classes.models.crafty_permissions import ( + crafty_permissions, + Enum_Permissions_Crafty, +) from app.classes.shared.helpers import helper from app.classes.shared.authentication import authentication logger = logging.getLogger(__name__) + class Users_Controller: - #************************************************************************************************ + # ************************************************************************************************ # Users Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_all_users(): return users_helper.get_all_users() @@ -59,26 +63,31 @@ class Users_Controller: if key == "user_id": continue elif key == "roles": - added_roles = user_data['roles'].difference(base_data['roles']) - removed_roles = base_data['roles'].difference(user_data['roles']) + added_roles = user_data["roles"].difference(base_data["roles"]) + removed_roles = base_data["roles"].difference(user_data["roles"]) elif key == "password": - if user_data['password'] is not None and user_data['password'] != "": - up_data['password'] = helper.encode_pass(user_data['password']) + if user_data["password"] is not None and user_data["password"] != "": + up_data["password"] = helper.encode_pass(user_data["password"]) elif base_data[key] != user_data[key]: up_data[key] = user_data[key] - up_data['last_update'] = helper.get_time_as_string() - up_data['lang'] = user_data['lang'] + up_data["last_update"] = helper.get_time_as_string() + up_data["lang"] = user_data["lang"] logger.debug(f"user: {user_data} +role:{added_roles} -role:{removed_roles}") for role in added_roles: users_helper.get_or_create(user_id=user_id, role_id=role) - permissions_mask = user_crafty_data.get('permissions_mask', '000') + permissions_mask = user_crafty_data.get("permissions_mask", "000") - if 'server_quantity' in user_crafty_data: - limit_server_creation = user_crafty_data['server_quantity'][ - Enum_Permissions_Crafty.Server_Creation.name] + if "server_quantity" in user_crafty_data: + limit_server_creation = user_crafty_data["server_quantity"][ + Enum_Permissions_Crafty.Server_Creation.name + ] - limit_user_creation = user_crafty_data['server_quantity'][Enum_Permissions_Crafty.User_Config.name] - limit_role_creation = user_crafty_data['server_quantity'][Enum_Permissions_Crafty.Roles_Config.name] + limit_user_creation = user_crafty_data["server_quantity"][ + Enum_Permissions_Crafty.User_Config.name + ] + limit_role_creation = user_crafty_data["server_quantity"][ + Enum_Permissions_Crafty.Roles_Config.name + ] else: limit_server_creation = 0 limit_user_creation = 0 @@ -89,19 +98,44 @@ class Users_Controller: permissions_mask, limit_server_creation, limit_user_creation, - limit_role_creation) + limit_role_creation, + ) users_helper.delete_user_roles(user_id, removed_roles) users_helper.update_user(user_id, up_data) @staticmethod - def add_user(username, password, email="default@example.com", enabled: bool = True, superuser: bool = False): - return users_helper.add_user(username, password=password, email=email, enabled=enabled, superuser=superuser) + def add_user( + username, + password, + email="default@example.com", + enabled: bool = True, + superuser: bool = False, + ): + return users_helper.add_user( + username, + password=password, + email=email, + enabled=enabled, + superuser=superuser, + ) @staticmethod - def add_rawpass_user(username, password, email="default@example.com", enabled: bool = True, superuser: bool = False): - return users_helper.add_rawpass_user(username, password=password, email=email, enabled=enabled, superuser=superuser) + def add_rawpass_user( + username, + password, + email="default@example.com", + enabled: bool = True, + superuser: bool = False, + ): + return users_helper.add_rawpass_user( + username, + password=password, + email=email, + enabled=enabled, + superuser=superuser, + ) @staticmethod def remove_user(user_id): @@ -122,7 +156,7 @@ class Users_Controller: @staticmethod def get_user_id_by_api_token(token: str) -> str: token_data = authentication.check_no_iat(token) - return token_data['user_id'] + return token_data["user_id"] @staticmethod def get_user_by_api_token(token: str): @@ -166,10 +200,16 @@ class Users_Controller: return users_helper.get_user_api_key(key_id) @staticmethod - def add_user_api_key(name: str, user_id: str, superuser: bool = False, - server_permissions_mask: Optional[str] = None, - crafty_permissions_mask: Optional[str] = None): - return users_helper.add_user_api_key(name, user_id, superuser, server_permissions_mask, crafty_permissions_mask) + def add_user_api_key( + name: str, + user_id: str, + superuser: bool = False, + server_permissions_mask: Optional[str] = None, + crafty_permissions_mask: Optional[str] = None, + ): + return users_helper.add_user_api_key( + name, user_id, superuser, server_permissions_mask, crafty_permissions_mask + ) @staticmethod def delete_user_api_keys(user_id: str): diff --git a/app/classes/minecraft/bedrock_ping.py b/app/classes/minecraft/bedrock_ping.py index 1d2a8c99..c1d44a35 100644 --- a/app/classes/minecraft/bedrock_ping.py +++ b/app/classes/minecraft/bedrock_ping.py @@ -3,21 +3,22 @@ import socket import time import psutil + class BedrockPing: - magic = b'\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78' - fields = { # (len, signed) + magic = b"\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78" + fields = { # (len, signed) "byte": (1, False), "long": (8, True), "ulong": (8, False), "magic": (16, False), "short": (2, True), - "ushort": (2, False), #unsigned short - "string": (2, False), #strlen is ushort + "ushort": (2, False), # unsigned short + "string": (2, False), # strlen is ushort "bool": (1, False), "address": (7, False), - "uint24le": (3, False) + "uint24le": (3, False), } - byte_order = 'big' + byte_order = "big" def __init__(self, bedrock_addr, bedrock_port, client_guid=0, timeout=5): self.addr = bedrock_addr @@ -36,51 +37,61 @@ class BedrockPing: @staticmethod def __slice(in_bytes, pattern): ret = [] - bi = 0 # bytes index - pi = 0 # pattern index + bi = 0 # bytes index + pi = 0 # pattern index while bi < len(in_bytes): try: f = BedrockPing.fields[pattern[pi]] except IndexError as index_error: - raise IndexError("Ran out of pattern with additional bytes remaining") from index_error + raise IndexError( + "Ran out of pattern with additional bytes remaining" + ) from index_error if pattern[pi] == "string": - shl = f[0] # string header length - sl = int.from_bytes(in_bytes[bi:bi+shl], BedrockPing.byte_order, signed=f[1]) # string length - l = shl+sl - ret.append(in_bytes[bi+shl:bi+shl+sl].decode('ascii')) + shl = f[0] # string header length + sl = int.from_bytes( + in_bytes[bi : bi + shl], BedrockPing.byte_order, signed=f[1] + ) # string length + l = shl + sl + ret.append(in_bytes[bi + shl : bi + shl + sl].decode("ascii")) elif pattern[pi] == "magic": - l = f[0] # length of field - ret.append(in_bytes[bi:bi+l]) + l = f[0] # length of field + ret.append(in_bytes[bi : bi + l]) else: - l = f[0] # length of field - ret.append(int.from_bytes(in_bytes[bi:bi+l], BedrockPing.byte_order, signed=f[1])) - bi+=l - pi+=1 + l = f[0] # length of field + ret.append( + int.from_bytes( + in_bytes[bi : bi + l], BedrockPing.byte_order, signed=f[1] + ) + ) + bi += l + pi += 1 return ret @staticmethod def __get_time(): - #return time.time_ns() // 1000000 + # return time.time_ns() // 1000000 return time.perf_counter_ns() // 1000000 def __sendping(self): - pack_id = BedrockPing.__byter(0x01, 'byte') - now = BedrockPing.__byter(BedrockPing.__get_time(), 'ulong') + pack_id = BedrockPing.__byter(0x01, "byte") + now = BedrockPing.__byter(BedrockPing.__get_time(), "ulong") guid = self.guid_bytes - d2s = pack_id+now+BedrockPing.magic+guid - #print("S:", d2s) + d2s = pack_id + now + BedrockPing.magic + guid + # print("S:", d2s) self.sock.sendto(d2s, (self.addr, self.port)) def __recvpong(self): data = self.sock.recv(4096) - if data[0] == 0x1c: + if data[0] == 0x1C: ret = {} - sliced = BedrockPing.__slice(data,["byte","ulong","ulong","magic","string"]) + sliced = BedrockPing.__slice( + data, ["byte", "ulong", "ulong", "magic", "string"] + ) if sliced[3] != BedrockPing.magic: raise ValueError(f"Incorrect magic received ({sliced[3]})") ret["server_guid"] = sliced[2] ret["server_string_raw"] = sliced[4] - server_info = sliced[4].split(';') + server_info = sliced[4].split(";") ret["server_edition"] = server_info[0] ret["server_motd"] = (server_info[1], server_info[7]) ret["server_protocol_version"] = server_info[2] @@ -103,5 +114,7 @@ class BedrockPing: self.__sendping() return self.__recvpong() except ValueError as e: - print(f"E: {e}, checking next packet. Retries remaining: {rtr}/{retries}") + print( + f"E: {e}, checking next packet. Retries remaining: {rtr}/{retries}" + ) rtr -= 1 diff --git a/app/classes/minecraft/mc_ping.py b/app/classes/minecraft/mc_ping.py index 1e4e6b7e..b540fac5 100644 --- a/app/classes/minecraft/mc_ping.py +++ b/app/classes/minecraft/mc_ping.py @@ -13,24 +13,25 @@ from app.classes.shared.console import console logger = logging.getLogger(__name__) + class Server: def __init__(self, data): - self.description = data.get('description') + self.description = data.get("description") # print(self.description) if isinstance(self.description, dict): # cat server if "translate" in self.description: - self.description = self.description['translate'] + self.description = self.description["translate"] # waterfall / bungee - elif 'extra' in self.description: + elif "extra" in self.description: lines = [] description = self.description - if 'extra' in description.keys(): - for e in description['extra']: - #Conversion format code needed only for Java Version + if "extra" in description.keys(): + for e in description["extra"]: + # Conversion format code needed only for Java Version lines.append(get_code_format("reset")) if "bold" in e.keys(): lines.append(get_code_format("bold")) @@ -43,36 +44,36 @@ class Server: if "obfuscated" in e.keys(): lines.append(get_code_format("obfuscated")) if "color" in e.keys(): - lines.append(get_code_format(e['color'])) - #Then append the text + lines.append(get_code_format(e["color"])) + # Then append the text if "text" in e.keys(): - if e['text'] == '\n': + if e["text"] == "\n": lines.append("ยงยง") else: - lines.append(e['text']) + lines.append(e["text"]) total_text = " ".join(lines) self.description = total_text # normal MC else: - self.description = self.description['text'] + self.description = self.description["text"] - self.icon = base64.b64decode(data.get('favicon', '')[22:]) + self.icon = base64.b64decode(data.get("favicon", "")[22:]) try: - self.players = Players(data['players']).report() + self.players = Players(data["players"]).report() except KeyError: logger.error("Error geting player information key error") self.players = [] - self.version = data['version']['name'] - self.protocol = data['version']['protocol'] + self.version = data["version"]["name"] + self.protocol = data["version"]["protocol"] class Players(list): def __init__(self, data): - super().__init__(Player(x) for x in data.get('sample', [])) - self.max = data['max'] - self.online = data['online'] + super().__init__(Player(x) for x in data.get("sample", [])) + self.max = data["max"] + self.online = data["online"] def report(self): players = [] @@ -80,35 +81,34 @@ class Players(list): for x in self: players.append(str(x)) - r_data = { - 'online': self.online, - 'max': self.max, - 'players': players - } + r_data = {"online": self.online, "max": self.max, "players": players} return json.dumps(r_data) class Player: def __init__(self, data): - self.id = data['id'] - self.name = data['name'] + self.id = data["id"] + self.name = data["name"] def __str__(self): return self.name + def get_code_format(format_name): root_dir = os.path.abspath(os.path.curdir) - format_file = os.path.join(root_dir, 'app', 'config', 'motd_format.json') + format_file = os.path.join(root_dir, "app", "config", "motd_format.json") try: - with open(format_file, "r", encoding='utf-8') as f: + with open(format_file, "r", encoding="utf-8") as f: data = json.load(f) if format_name in data.keys(): return data.get(format_name) else: logger.error(f"Format MOTD Error: format name {format_name} does not exist") - console.error(f"Format MOTD Error: format name {format_name} does not exist") + console.error( + f"Format MOTD Error: format name {format_name} does not exist" + ) return "" except Exception as e: @@ -128,10 +128,10 @@ def ping(ip, port): if not k: return 0 k = k[0] - i |= (k & 0x7f) << (j * 7) + i |= (k & 0x7F) << (j * 7) j += 1 if j > 5: - raise ValueError('var_int too big') + raise ValueError("var_int too big") if not k & 0x80: return i @@ -143,15 +143,15 @@ def ping(ip, port): return False try: - host = ip.encode('utf-8') - data = b'' # wiki.vg/Server_List_Ping - data += b'\x00' # packet ID - data += b'\x04' # protocol variant - data += struct.pack('>b', len(host)) + host - data += struct.pack('>H', port) - data += b'\x01' # next state - data = struct.pack('>b', len(data)) + data - sock.sendall(data + b'\x01\x00') # handshake + status ping + host = ip.encode("utf-8") + data = b"" # wiki.vg/Server_List_Ping + data += b"\x00" # packet ID + data += b"\x04" # protocol variant + data += struct.pack(">b", len(host)) + host + data += struct.pack(">H", port) + data += b"\x01" # next state + data = struct.pack(">b", len(data)) + data + sock.sendall(data + b"\x01\x00") # handshake + status ping length = read_var_int() # full packet length if length < 10: if length < 0: @@ -161,7 +161,7 @@ def ping(ip, port): sock.recv(1) # packet type, 0 for pings length = read_var_int() # string length - data = b'' + data = b"" while len(data) != length: chunk = sock.recv(length - len(data)) if not chunk: @@ -176,12 +176,13 @@ def ping(ip, port): finally: sock.close() + # For the rest of requests see wiki.vg/Protocol def ping_bedrock(ip, port): rd = random.Random() try: - #pylint: disable=consider-using-f-string - rd.seed(''.join(re.findall('..', '%012x' % uuid.getnode()))) + # pylint: disable=consider-using-f-string + rd.seed("".join(re.findall("..", "%012x" % uuid.getnode()))) client_guid = uuid.UUID(int=rd.getrandbits(32)).int except: client_guid = 0 diff --git a/app/classes/minecraft/server_props.py b/app/classes/minecraft/server_props.py index 027e11ac..c70652de 100644 --- a/app/classes/minecraft/server_props.py +++ b/app/classes/minecraft/server_props.py @@ -1,30 +1,30 @@ import pprint import os -class ServerProps: +class ServerProps: def __init__(self, filepath): self.filepath = filepath self.props = self._parse() def _parse(self): """Loads and parses the file specified in self.filepath""" - with open(self.filepath, encoding='utf-8') as fp: + with open(self.filepath, encoding="utf-8") as fp: line = fp.readline() d = {} if os.path.exists(".header"): os.remove(".header") while line: - if '#' != line[0]: + if "#" != line[0]: s = line - s1 = s[:s.find('=')] - if '\n' in s: - s2 = s[s.find('=')+1:s.find('\n')] + s1 = s[: s.find("=")] + if "\n" in s: + s2 = s[s.find("=") + 1 : s.find("\n")] else: - s2 = s[s.find('=')+1:] + s2 = s[s.find("=") + 1 :] d[s1] = s2 else: - with open(".header", "a+", encoding='utf-8') as h: + with open(".header", "a+", encoding="utf-8") as h: h.write(line) line = fp.readline() return d @@ -47,9 +47,9 @@ class ServerProps: def save(self): """Writes to the new file""" - with open(self.filepath, "a+", encoding='utf-8') as f: + with open(self.filepath, "a+", encoding="utf-8") as f: f.truncate(0) - with open(".header", encoding='utf-8') as header: + with open(".header", encoding="utf-8") as header: line = header.readline() while line: f.write(line) diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py index 46e040a5..380dbd38 100644 --- a/app/classes/minecraft/serverjars.py +++ b/app/classes/minecraft/serverjars.py @@ -18,8 +18,8 @@ try: except ModuleNotFoundError as err: helper.auto_installer_fix(err) -class ServerJars: +class ServerJars: def __init__(self): self.base_url = "https://serverjars.com" @@ -41,8 +41,8 @@ class ServerJars: logger.error(f"Unable to parse serverjar.com api result due to error: {e}") return {} - api_result = api_data.get('status') - api_response = api_data.get('response', {}) + api_result = api_data.get("status") + api_response = api_data.get("response", {}) if api_result != "success": logger.error(f"Api returned a failed status: {api_result}") @@ -55,7 +55,7 @@ class ServerJars: cache_file = helper.serverjar_cache cache = {} try: - with open(cache_file, "r", encoding='utf-8') as f: + with open(cache_file, "r", encoding="utf-8") as f: cache = json.load(f) except Exception as e: @@ -65,7 +65,7 @@ class ServerJars: def get_serverjar_data(self): data = self._read_cache() - return data.get('servers') + return data.get("servers") def get_serverjar_data_sorted(self): data = self.get_serverjar_data() @@ -80,10 +80,10 @@ class ServerJars: try: return int(x) except ValueError: - temp = x.split('-') + temp = x.split("-") return to_int(temp[0]) + str_to_int(temp[1]) / 100000 - sort_key_fn = lambda x: [to_int(y) for y in x.split('.')] + sort_key_fn = lambda x: [to_int(y) for y in x.split(".")] for key in data.keys(): data[key] = sorted(data[key], key=sort_key_fn) @@ -125,10 +125,7 @@ class ServerJars: if cache_old: logger.info("Cache file is over 1 day old, refreshing") now = datetime.now() - data = { - 'last_refreshed': now.strftime("%m/%d/%Y, %H:%M:%S"), - 'servers': {} - } + data = {"last_refreshed": now.strftime("%m/%d/%Y, %H:%M:%S"), "servers": {}} jar_types = self._get_server_type_list() @@ -141,51 +138,51 @@ class ServerJars: versions = self._get_jar_details(s) # add these versions (a list) to the dict with a key of the server type - data['servers'].update({ - s: versions - }) + data["servers"].update({s: versions}) # save our cache try: - with open(cache_file, "w", encoding='utf-8') as f: + with open(cache_file, "w", encoding="utf-8") as f: f.write(json.dumps(data, indent=4)) logger.info("Cache file refreshed") except Exception as e: logger.error(f"Unable to update serverjars.com cache file: {e}") - def _get_jar_details(self, jar_type='servers'): - url = f'/api/fetchAll/{jar_type}' + def _get_jar_details(self, jar_type="servers"): + url = f"/api/fetchAll/{jar_type}" response = self._get_api_result(url) temp = [] for v in response: - temp.append(v.get('version')) - time.sleep(.5) + temp.append(v.get("version")) + time.sleep(0.5) return temp def _get_server_type_list(self): - url = '/api/fetchTypes/' + url = "/api/fetchTypes/" response = self._get_api_result(url) return response def download_jar(self, server, version, path, server_id): - update_thread = threading.Thread(target=self.a_download_jar, daemon=True, args=(server, version, path, server_id)) + update_thread = threading.Thread( + target=self.a_download_jar, + daemon=True, + args=(server, version, path, server_id), + ) update_thread.start() def a_download_jar(self, server, version, path, server_id): - #delaying download for server register to finish + # delaying download for server register to finish time.sleep(3) fetch_url = f"{self.base_url}/api/fetchJar/{server}/{version}" server_users = server_permissions.get_server_user_list(server_id) - - #We need to make sure the server is registered before we submit a db update for it's stats. + # We need to make sure the server is registered before we submit a db update for it's stats. while True: try: Servers_Controller.set_download(server_id) for user in server_users: - websocket_helper.broadcast_user(user, 'send_start_reload', { - }) + websocket_helper.broadcast_user(user, "send_start_reload", {}) break except: @@ -194,25 +191,27 @@ class ServerJars: # open a file stream with requests.get(fetch_url, timeout=2, stream=True) as r: try: - with open(path, 'wb') as output: + with open(path, "wb") as output: shutil.copyfileobj(r.raw, output) Servers_Controller.finish_download(server_id) for user in server_users: - websocket_helper.broadcast_user(user, 'notification', "Executable download finished") + websocket_helper.broadcast_user( + user, "notification", "Executable download finished" + ) time.sleep(3) - websocket_helper.broadcast_user(user, 'send_start_reload', { - }) + websocket_helper.broadcast_user(user, "send_start_reload", {}) return True except Exception as e: logger.error(f"Unable to save jar to {path} due to error:{e}") Servers_Controller.finish_download(server_id) server_users = server_permissions.get_server_user_list(server_id) for user in server_users: - websocket_helper.broadcast_user(user, 'notification', "Executable download finished") + websocket_helper.broadcast_user( + user, "notification", "Executable download finished" + ) time.sleep(3) - websocket_helper.broadcast_user(user, 'send_start_reload', { - }) + websocket_helper.broadcast_user(user, "send_start_reload", {}) return False diff --git a/app/classes/minecraft/stats.py b/app/classes/minecraft/stats.py index 456213ce..3fcc780e 100644 --- a/app/classes/minecraft/stats.py +++ b/app/classes/minecraft/stats.py @@ -11,8 +11,8 @@ from app.classes.shared.helpers import helper logger = logging.getLogger(__name__) -class Stats: +class Stats: def __init__(self, controller): self.controller = controller @@ -24,30 +24,26 @@ class Stats: except NotImplementedError: cpu_freq = psutil._common.scpufreq(current=0, min=0, max=0) node_stats = { - 'boot_time': str(boot_time), - 'cpu_usage': psutil.cpu_percent(interval=0.5) / psutil.cpu_count(), - 'cpu_count': psutil.cpu_count(), - 'cpu_cur_freq': round(cpu_freq[0], 2), - 'cpu_max_freq': cpu_freq[2], - 'mem_percent': psutil.virtual_memory()[2], - 'mem_usage': helper.human_readable_file_size(psutil.virtual_memory()[3]), - 'mem_total': helper.human_readable_file_size(psutil.virtual_memory()[0]), - 'disk_data': self._all_disk_usage() + "boot_time": str(boot_time), + "cpu_usage": psutil.cpu_percent(interval=0.5) / psutil.cpu_count(), + "cpu_count": psutil.cpu_count(), + "cpu_cur_freq": round(cpu_freq[0], 2), + "cpu_max_freq": cpu_freq[2], + "mem_percent": psutil.virtual_memory()[2], + "mem_usage": helper.human_readable_file_size(psutil.virtual_memory()[3]), + "mem_total": helper.human_readable_file_size(psutil.virtual_memory()[0]), + "disk_data": self._all_disk_usage(), } - #server_stats = self.get_servers_stats() - #data['servers'] = server_stats - data['node_stats'] = node_stats + # server_stats = self.get_servers_stats() + # data['servers'] = server_stats + data["node_stats"] = node_stats return data @staticmethod def _get_process_stats(process): if process is None: - process_stats = { - 'cpu_usage': 0, - 'memory_usage': 0, - 'mem_percentage': 0 - } + process_stats = {"cpu_usage": 0, "memory_usage": 0, "mem_percentage": 0} return process_stats else: process_pid = process.pid @@ -63,19 +59,21 @@ class Stats: # this is a faster way of getting data for a process with p.oneshot(): process_stats = { - 'cpu_usage': real_cpu, - 'memory_usage': helper.human_readable_file_size(p.memory_info()[0]), - 'mem_percentage': round(p.memory_percent(), 0) + "cpu_usage": real_cpu, + "memory_usage": helper.human_readable_file_size(p.memory_info()[0]), + "mem_percentage": round(p.memory_percent(), 0), } return process_stats except Exception as e: - logger.error(f"Unable to get process details for pid: {process_pid} due to error: {e}") + logger.error( + f"Unable to get process details for pid: {process_pid} due to error: {e}" + ) # Dummy Data process_stats = { - 'cpu_usage': 0, - 'memory_usage': 0, + "cpu_usage": 0, + "memory_usage": 0, } return process_stats @@ -87,7 +85,7 @@ class Stats: for part in psutil.disk_partitions(all=False): if helper.is_os_windows(): - if 'cdrom' in part.opts or part.fstype == '': + if "cdrom" in part.opts or part.fstype == "": # skip cd-rom drives with no disk in it; they may raise # ENOENT, pop-up a Windows GUI error for a non-ready # partition or just hang. @@ -95,13 +93,13 @@ class Stats: usage = psutil.disk_usage(part.mountpoint) disk_data.append( { - 'device': part.device, - 'total': helper.human_readable_file_size(usage.total), - 'used': helper.human_readable_file_size(usage.used), - 'free': helper.human_readable_file_size(usage.free), - 'percent_used': int(usage.percent), - 'fs': part.fstype, - 'mount': part.mountpoint + "device": part.device, + "total": helper.human_readable_file_size(usage.total), + "used": helper.human_readable_file_size(usage.used), + "free": helper.human_readable_file_size(usage.free), + "percent_used": int(usage.percent), + "fs": part.fstype, + "mount": part.mountpoint, } ) @@ -128,22 +126,20 @@ class Stats: # server_settings = server.get('server_settings', {}) # server_data = server.get('server_data_obj', {}) - # TODO: search server properties file for possible override of 127.0.0.1 - internal_ip = server['server_ip'] - server_port = server['server_port'] + internal_ip = server["server_ip"] + server_port = server["server_port"] logger.debug("Pinging {internal_ip} on port {server_port}") - if servers_helper.get_server_type_by_id(server_id) != 'minecraft-bedrock': + if servers_helper.get_server_type_by_id(server_id) != "minecraft-bedrock": int_mc_ping = ping(internal_ip, int(server_port)) - ping_data = {} # if we got a good ping return, let's parse it if int_mc_ping: ping_data = Stats.parse_server_ping(int_mc_ping) - return ping_data['players'] + return ping_data["players"] return [] @staticmethod @@ -156,21 +152,20 @@ class Stats: except Exception as e: logger.info(f"Unable to read json from ping_obj: {e}") - try: server_icon = base64.encodebytes(ping_obj.icon) - server_icon = server_icon.decode('utf-8') - except Exception as e: + server_icon = server_icon.decode("utf-8") + except Exception as e: server_icon = False logger.info(f"Unable to read the server icon : {e}") ping_data = { - 'online': online_stats.get("online", 0), - 'max': online_stats.get('max', 0), - 'players': online_stats.get('players', 0), - 'server_description': ping_obj.description, - 'server_version': ping_obj.version, - 'server_icon': server_icon + "online": online_stats.get("online", 0), + "max": online_stats.get("max", 0), + "players": online_stats.get("players", 0), + "server_description": ping_obj.description, + "server_version": ping_obj.version, + "server_icon": server_icon, } return ping_data @@ -179,59 +174,59 @@ class Stats: def parse_server_RakNet_ping(ping_obj: object): try: - server_icon = base64.encodebytes(ping_obj['icon']) - except Exception as e: + server_icon = base64.encodebytes(ping_obj["icon"]) + except Exception as e: server_icon = False logger.info(f"Unable to read the server icon : {e}") ping_data = { - 'online': ping_obj['server_player_count'], - 'max': ping_obj['server_player_max'], - 'players': [], - 'server_description': ping_obj['server_edition'], - 'server_version': ping_obj['server_version_name'], - 'server_icon': server_icon + "online": ping_obj["server_player_count"], + "max": ping_obj["server_player_max"], + "players": [], + "server_description": ping_obj["server_edition"], + "server_version": ping_obj["server_version_name"], + "server_icon": server_icon, } - return ping_data - def record_stats(self): stats_to_send = self.get_node_stats() - node_stats = stats_to_send.get('node_stats') + node_stats = stats_to_send.get("node_stats") - Host_Stats.insert({ - Host_Stats.boot_time: node_stats.get('boot_time', "Unknown"), - Host_Stats.cpu_usage: round(node_stats.get('cpu_usage', 0), 2), - Host_Stats.cpu_cores: node_stats.get('cpu_count', 0), - Host_Stats.cpu_cur_freq: node_stats.get('cpu_cur_freq', 0), - Host_Stats.cpu_max_freq: node_stats.get('cpu_max_freq', 0), - Host_Stats.mem_usage: node_stats.get('mem_usage', "0 MB"), - Host_Stats.mem_percent: node_stats.get('mem_percent', 0), - Host_Stats.mem_total: node_stats.get('mem_total', "0 MB"), - Host_Stats.disk_json: node_stats.get('disk_data', '{}') - }).execute() + Host_Stats.insert( + { + Host_Stats.boot_time: node_stats.get("boot_time", "Unknown"), + Host_Stats.cpu_usage: round(node_stats.get("cpu_usage", 0), 2), + Host_Stats.cpu_cores: node_stats.get("cpu_count", 0), + Host_Stats.cpu_cur_freq: node_stats.get("cpu_cur_freq", 0), + Host_Stats.cpu_max_freq: node_stats.get("cpu_max_freq", 0), + Host_Stats.mem_usage: node_stats.get("mem_usage", "0 MB"), + Host_Stats.mem_percent: node_stats.get("mem_percent", 0), + Host_Stats.mem_total: node_stats.get("mem_total", "0 MB"), + Host_Stats.disk_json: node_stats.get("disk_data", "{}"), + } + ).execute() -# server_stats = stats_to_send.get('servers')# -# -# for server in server_stats: -# Server_Stats.insert({ -# Server_Stats.server_id: server.get('id', 0), -# Server_Stats.started: server.get('started', ""), -# Server_Stats.running: server.get('running', False), -# Server_Stats.cpu: server.get('cpu', 0), -# Server_Stats.mem: server.get('mem', 0), -# Server_Stats.mem_percent: server.get('mem_percent', 0), -# Server_Stats.world_name: server.get('world_name', ""), -# Server_Stats.world_size: server.get('world_size', ""), -# Server_Stats.server_port: server.get('server_port', ""), -# Server_Stats.int_ping_results: server.get('int_ping_results', False), -# Server_Stats.online: server.get("online", False), -# Server_Stats.max: server.get("max", False), -# Server_Stats.players: server.get("players", False), -# Server_Stats.desc: server.get("desc", False), -# Server_Stats.version: server.get("version", False) -# }).execute() + # server_stats = stats_to_send.get('servers')# + # + # for server in server_stats: + # Server_Stats.insert({ + # Server_Stats.server_id: server.get('id', 0), + # Server_Stats.started: server.get('started', ""), + # Server_Stats.running: server.get('running', False), + # Server_Stats.cpu: server.get('cpu', 0), + # Server_Stats.mem: server.get('mem', 0), + # Server_Stats.mem_percent: server.get('mem_percent', 0), + # Server_Stats.world_name: server.get('world_name', ""), + # Server_Stats.world_size: server.get('world_size', ""), + # Server_Stats.server_port: server.get('server_port', ""), + # Server_Stats.int_ping_results: server.get('int_ping_results', False), + # Server_Stats.online: server.get("online", False), + # Server_Stats.max: server.get("max", False), + # Server_Stats.players: server.get("players", False), + # Server_Stats.desc: server.get("desc", False), + # Server_Stats.version: server.get("version", False) + # }).execute() # delete old data max_age = helper.get_setting("history_max_age") @@ -239,4 +234,6 @@ class Stats: last_week = now.day - max_age Host_Stats.delete().where(Host_Stats.time < last_week).execute() + + # Server_Stats.delete().where(Server_Stats.created < last_week).execute() diff --git a/app/classes/models/crafty_permissions.py b/app/classes/models/crafty_permissions.py index 13a655e5..46b02315 100644 --- a/app/classes/models/crafty_permissions.py +++ b/app/classes/models/crafty_permissions.py @@ -5,25 +5,32 @@ from app.classes.shared.permission_helper import permission_helper from app.classes.models.users import Users, ApiKeys try: - from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, IntegerField, DoesNotExist + from peewee import ( + SqliteDatabase, + Model, + ForeignKeyField, + CharField, + IntegerField, + DoesNotExist, + ) from enum import Enum except ModuleNotFoundError as e: helper.auto_installer_fix(e) logger = logging.getLogger(__name__) -peewee_logger = logging.getLogger('peewee') +peewee_logger = logging.getLogger("peewee") peewee_logger.setLevel(logging.INFO) -database = SqliteDatabase(helper.db_path, pragmas = { - 'journal_mode': 'wal', - 'cache_size': -1024 * 10}) +database = SqliteDatabase( + helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} +) -#************************************************************************************************ +# ************************************************************************************************ # User_Crafty Class -#************************************************************************************************ +# ************************************************************************************************ class User_Crafty(Model): - user_id = ForeignKeyField(Users, backref='users_crafty') + user_id = ForeignKeyField(Users, backref="users_crafty") permissions = CharField(default="00000000") limit_server_creation = IntegerField(default=-1) limit_user_creation = IntegerField(default=0) @@ -33,22 +40,24 @@ class User_Crafty(Model): created_role = IntegerField(default=0) class Meta: - table_name = 'user_crafty' + table_name = "user_crafty" database = database -#************************************************************************************************ + +# ************************************************************************************************ # Crafty Permissions Class -#************************************************************************************************ +# ************************************************************************************************ class Enum_Permissions_Crafty(Enum): Server_Creation = 0 User_Config = 1 Roles_Config = 2 + class Permissions_Crafty: - #************************************************************************************************ + # ************************************************************************************************ # Crafty Permissions Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_permissions_list(): permissions_list = [] @@ -67,15 +76,17 @@ class Permissions_Crafty: @staticmethod def has_permission(permission_mask, permission_tested: Enum_Permissions_Crafty): result = False - if permission_mask[permission_tested.value] == '1': + if permission_mask[permission_tested.value] == "1": result = True return result @staticmethod - def set_permission(permission_mask, permission_tested: Enum_Permissions_Crafty, value): + def set_permission( + permission_mask, permission_tested: Enum_Permissions_Crafty, value + ): l = list(permission_mask) l[permission_tested.value] = str(value) - permission_mask = ''.join(l) + permission_mask = "".join(l) return permission_mask @staticmethod @@ -84,7 +95,7 @@ class Permissions_Crafty: @staticmethod def get_crafty_permissions_mask(user_id): - permissions_mask = '' + permissions_mask = "" user_crafty = crafty_permissions.get_User_Crafty(user_id) permissions_mask = user_crafty.permissions return permissions_mask @@ -108,49 +119,65 @@ class Permissions_Crafty: } return quantity_list - #************************************************************************************************ + # ************************************************************************************************ # User_Crafty Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_User_Crafty(user_id): try: - user_crafty = User_Crafty.select().where(User_Crafty.user_id == user_id).get() + user_crafty = ( + User_Crafty.select().where(User_Crafty.user_id == user_id).get() + ) except DoesNotExist: - user_crafty = User_Crafty.insert({ - User_Crafty.user_id: user_id, - User_Crafty.permissions: "000", - User_Crafty.limit_server_creation: 0, - User_Crafty.limit_user_creation: 0, - User_Crafty.limit_role_creation: 0, - User_Crafty.created_server: 0, - User_Crafty.created_user: 0, - User_Crafty.created_role: 0, - }).execute() + user_crafty = User_Crafty.insert( + { + User_Crafty.user_id: user_id, + User_Crafty.permissions: "000", + User_Crafty.limit_server_creation: 0, + User_Crafty.limit_user_creation: 0, + User_Crafty.limit_role_creation: 0, + User_Crafty.created_server: 0, + User_Crafty.created_user: 0, + User_Crafty.created_role: 0, + } + ).execute() user_crafty = crafty_permissions.get_User_Crafty(user_id) return user_crafty @staticmethod def add_user_crafty(user_id, uc_permissions): - user_crafty = User_Crafty.insert({User_Crafty.user_id: user_id, User_Crafty.permissions: uc_permissions}).execute() + user_crafty = User_Crafty.insert( + {User_Crafty.user_id: user_id, User_Crafty.permissions: uc_permissions} + ).execute() return user_crafty @staticmethod - def add_or_update_user(user_id, permissions_mask, limit_server_creation, limit_user_creation, limit_role_creation): + def add_or_update_user( + user_id, + permissions_mask, + limit_server_creation, + limit_user_creation, + limit_role_creation, + ): try: - user_crafty = User_Crafty.select().where(User_Crafty.user_id == user_id).get() + user_crafty = ( + User_Crafty.select().where(User_Crafty.user_id == user_id).get() + ) user_crafty.permissions = permissions_mask user_crafty.limit_server_creation = limit_server_creation user_crafty.limit_user_creation = limit_user_creation user_crafty.limit_role_creation = limit_role_creation User_Crafty.save(user_crafty) except: - User_Crafty.insert({ - User_Crafty.user_id: user_id, - User_Crafty.permissions: permissions_mask, - User_Crafty.limit_server_creation: limit_server_creation, - User_Crafty.limit_user_creation: limit_user_creation, - User_Crafty.limit_role_creation: limit_role_creation - }).execute() + User_Crafty.insert( + { + User_Crafty.user_id: user_id, + User_Crafty.permissions: permissions_mask, + User_Crafty.limit_server_creation: limit_server_creation, + User_Crafty.limit_user_creation: limit_user_creation, + User_Crafty.limit_role_creation: limit_role_creation, + } + ).execute() @staticmethod def get_created_quantity_list(user_id): @@ -173,7 +200,10 @@ class Permissions_Crafty: can = crafty_permissions.has_permission(user_crafty.permissions, permission) limit_list = crafty_permissions.get_permission_quantity_list(user_id) quantity_list = crafty_permissions.get_created_quantity_list(user_id) - return can and ((quantity_list[permission.name] < limit_list[permission.name]) or limit_list[permission.name] == -1 ) + return can and ( + (quantity_list[permission.name] < limit_list[permission.name]) + or limit_list[permission.name] == -1 + ) @staticmethod def add_server_creation(user_id): @@ -188,12 +218,15 @@ class Permissions_Crafty: if user.superuser and key.superuser: return crafty_permissions.get_permissions_list() else: - user_permissions_mask = crafty_permissions.get_crafty_permissions_mask(user.user_id) + user_permissions_mask = crafty_permissions.get_crafty_permissions_mask( + user.user_id + ) key_permissions_mask: str = key.crafty_permissions - permissions_mask = permission_helper.combine_masks(user_permissions_mask, key_permissions_mask) + permissions_mask = permission_helper.combine_masks( + user_permissions_mask, key_permissions_mask + ) permissions_list = crafty_permissions.get_permissions(permissions_mask) return permissions_list - crafty_permissions = Permissions_Crafty() diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 927f97b4..2e55bd49 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -9,38 +9,51 @@ from app.classes.shared.main_models import db_helper from app.classes.web.websocket_helper import websocket_helper try: - from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, IntegerField, DateTimeField, FloatField, TextField, AutoField, BooleanField + from peewee import ( + SqliteDatabase, + Model, + ForeignKeyField, + CharField, + IntegerField, + DateTimeField, + FloatField, + TextField, + AutoField, + BooleanField, + ) from playhouse.shortcuts import model_to_dict except ModuleNotFoundError as e: helper.auto_installer_fix(e) logger = logging.getLogger(__name__) -peewee_logger = logging.getLogger('peewee') +peewee_logger = logging.getLogger("peewee") peewee_logger.setLevel(logging.INFO) -database = SqliteDatabase(helper.db_path, pragmas = { - 'journal_mode': 'wal', - 'cache_size': -1024 * 10}) +database = SqliteDatabase( + helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} +) -#************************************************************************************************ +# ************************************************************************************************ # Audit_Log Class -#************************************************************************************************ +# ************************************************************************************************ class Audit_Log(Model): audit_id = AutoField() created = DateTimeField(default=datetime.datetime.now) user_name = CharField(default="") user_id = IntegerField(default=0, index=True) - source_ip = CharField(default='127.0.0.1') - server_id = IntegerField(default=None, index=True) # When auditing global events, use server ID 0 - log_msg = TextField(default='') + source_ip = CharField(default="127.0.0.1") + server_id = IntegerField( + default=None, index=True + ) # When auditing global events, use server ID 0 + log_msg = TextField(default="") class Meta: database = database -#************************************************************************************************ +# ************************************************************************************************ # Host_Stats Class -#************************************************************************************************ +# ************************************************************************************************ class Host_Stats(Model): time = DateTimeField(default=datetime.datetime.now, index=True) boot_time = CharField(default="") @@ -58,16 +71,16 @@ class Host_Stats(Model): database = database -#************************************************************************************************ +# ************************************************************************************************ # Commands Class -#************************************************************************************************ +# ************************************************************************************************ class Commands(Model): command_id = AutoField() created = DateTimeField(default=datetime.datetime.now) - server_id = ForeignKeyField(Servers, backref='server', index=True) - user = ForeignKeyField(Users, backref='user', index=True) - source_ip = CharField(default='127.0.0.1') - command = CharField(default='') + server_id = ForeignKeyField(Servers, backref="server", index=True) + user = ForeignKeyField(Users, backref="user", index=True) + source_ip = CharField(default="127.0.0.1") + command = CharField(default="") executed = BooleanField(default=False) class Meta: @@ -75,9 +88,9 @@ class Commands(Model): database = database -#************************************************************************************************ +# ************************************************************************************************ # Webhooks Class -#************************************************************************************************ +# ************************************************************************************************ class Webhooks(Model): id = AutoField() name = CharField(max_length=64, unique=True, index=True) @@ -91,12 +104,12 @@ class Webhooks(Model): database = database -#************************************************************************************************ +# ************************************************************************************************ # Schedules Class -#************************************************************************************************ +# ************************************************************************************************ class Schedules(Model): schedule_id = IntegerField(unique=True, primary_key=True) - server_id = ForeignKeyField(Servers, backref='schedule_server') + server_id = ForeignKeyField(Servers, backref="schedule_server") enabled = BooleanField() action = CharField() interval = IntegerField() @@ -110,44 +123,48 @@ class Schedules(Model): delay = IntegerField(default=0) class Meta: - table_name = 'schedules' + table_name = "schedules" database = database -#************************************************************************************************ +# ************************************************************************************************ # Backups Class -#************************************************************************************************ +# ************************************************************************************************ class Backups(Model): excluded_dirs = CharField(null=True) max_backups = IntegerField() - server_id = ForeignKeyField(Servers, backref='backups_server') + server_id = ForeignKeyField(Servers, backref="backups_server") compress = BooleanField(default=False) + class Meta: - table_name = 'backups' + table_name = "backups" database = database + class helpers_management: - #************************************************************************************************ + # ************************************************************************************************ # Host_Stats Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_latest_hosts_stats(): - #pylint: disable=no-member + # pylint: disable=no-member query = Host_Stats.select().order_by(Host_Stats.id.desc()).get() return model_to_dict(query) - #************************************************************************************************ + # ************************************************************************************************ # Commands Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def add_command(server_id, user_id, remote_ip, command): - Commands.insert({ - Commands.server_id: server_id, - Commands.user: user_id, - Commands.source_ip: remote_ip, - Commands.command: command - }).execute() + Commands.insert( + { + Commands.server_id: server_id, + Commands.user: user_id, + Commands.source_ip: remote_ip, + Commands.command: command, + } + ).execute() @staticmethod def get_unactioned_commands(): @@ -158,13 +175,13 @@ class helpers_management: def mark_command_complete(command_id=None): if command_id is not None: logger.debug(f"Marking Command {command_id} completed") - Commands.update({ - Commands.executed: True - }).where(Commands.command_id == command_id).execute() + Commands.update({Commands.executed: True}).where( + Commands.command_id == command_id + ).execute() - #************************************************************************************************ + # ************************************************************************************************ # Audit_Log Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_actity_log(): q = Audit_Log.select() @@ -179,22 +196,24 @@ class helpers_management: server_users = server_permissions.get_server_user_list(server_id) for user in server_users: - websocket_helper.broadcast_user(user,'notification', audit_msg) + websocket_helper.broadcast_user(user, "notification", audit_msg) - Audit_Log.insert({ - Audit_Log.user_name: user_data['username'], - Audit_Log.user_id: user_id, - Audit_Log.server_id: server_id, - Audit_Log.log_msg: audit_msg, - Audit_Log.source_ip: source_ip - }).execute() - #deletes records when they're more than 100 + Audit_Log.insert( + { + Audit_Log.user_name: user_data["username"], + Audit_Log.user_id: user_id, + Audit_Log.server_id: server_id, + Audit_Log.log_msg: audit_msg, + Audit_Log.source_ip: source_ip, + } + ).execute() + # deletes records when they're more than 100 ordered = Audit_Log.select().order_by(+Audit_Log.created) for item in ordered: - if not helper.get_setting('max_audit_entries'): + if not helper.get_setting("max_audit_entries"): max_entries = 300 else: - max_entries = helper.get_setting('max_audit_entries') + max_entries = helper.get_setting("max_audit_entries") if Audit_Log.select().count() > max_entries: Audit_Log.delete().where(Audit_Log.audit_id == item.audit_id).execute() else: @@ -202,28 +221,31 @@ class helpers_management: @staticmethod def add_to_audit_log_raw(user_name, user_id, server_id, log_msg, source_ip): - Audit_Log.insert({ - Audit_Log.user_name: user_name, - Audit_Log.user_id: user_id, - Audit_Log.server_id: server_id, - Audit_Log.log_msg: log_msg, - Audit_Log.source_ip: source_ip - }).execute() - #deletes records when they're more than 100 + Audit_Log.insert( + { + Audit_Log.user_name: user_name, + Audit_Log.user_id: user_id, + Audit_Log.server_id: server_id, + Audit_Log.log_msg: log_msg, + Audit_Log.source_ip: source_ip, + } + ).execute() + # deletes records when they're more than 100 ordered = Audit_Log.select().order_by(+Audit_Log.created) for item in ordered: - #configurable through app/config/config.json - if not helper.get_setting('max_audit_entries'): + # configurable through app/config/config.json + if not helper.get_setting("max_audit_entries"): max_entries = 300 else: - max_entries = helper.get_setting('max_audit_entries') + max_entries = helper.get_setting("max_audit_entries") if Audit_Log.select().count() > max_entries: Audit_Log.delete().where(Audit_Log.audit_id == item.audit_id).execute() else: return - #************************************************************************************************ + + # ************************************************************************************************ # Schedules Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def create_scheduled_task( server_id, @@ -235,24 +257,26 @@ class helpers_management: comment=None, enabled=True, one_time=False, - cron_string='* * * * *', + cron_string="* * * * *", parent=None, - delay=0): - sch_id = Schedules.insert({ - Schedules.server_id: server_id, - Schedules.action: action, - Schedules.enabled: enabled, - Schedules.interval: interval, - Schedules.interval_type: interval_type, - Schedules.start_time: start_time, - Schedules.command: command, - Schedules.comment: comment, - Schedules.one_time: one_time, - Schedules.cron_string: cron_string, - Schedules.parent: parent, - Schedules.delay: delay - - }).execute() + delay=0, + ): + sch_id = Schedules.insert( + { + Schedules.server_id: server_id, + Schedules.action: action, + Schedules.enabled: enabled, + Schedules.interval: interval, + Schedules.interval_type: interval_type, + Schedules.start_time: start_time, + Schedules.command: command, + Schedules.comment: comment, + Schedules.one_time: one_time, + Schedules.cron_string: cron_string, + Schedules.parent: parent, + Schedules.delay: delay, + } + ).execute() return sch_id @staticmethod @@ -282,7 +306,11 @@ class helpers_management: @staticmethod def get_child_schedules_by_server(schedule_id, server_id): - return Schedules.select().where(Schedules.server_id == server_id, Schedules.parent == schedule_id).execute() + return ( + Schedules.select() + .where(Schedules.server_id == server_id, Schedules.parent == schedule_id) + .execute() + ) @staticmethod def get_child_schedules(schedule_id): @@ -294,22 +322,24 @@ class helpers_management: @staticmethod def get_schedules_enabled(): - #pylint: disable=singleton-comparison + # pylint: disable=singleton-comparison return Schedules.select().where(Schedules.enabled == True).execute() - #************************************************************************************************ + # ************************************************************************************************ # Backups Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_backup_config(server_id): try: - row = Backups.select().where(Backups.server_id == server_id).join(Servers)[0] + row = ( + Backups.select().where(Backups.server_id == server_id).join(Servers)[0] + ) conf = { "backup_path": row.server_id.backup_path, "excluded_dirs": row.excluded_dirs, "max_backups": row.max_backups, "server_id": row.server_id.server_id, - "compress": row.compress + "compress": row.compress, } except IndexError: conf = { @@ -322,7 +352,13 @@ class helpers_management: return conf @staticmethod - def set_backup_config(server_id: int, backup_path: str = None, max_backups: int = None, excluded_dirs: list = None, compress: bool = False): + def set_backup_config( + server_id: int, + backup_path: str = None, + max_backups: int = None, + excluded_dirs: list = None, + compress: bool = False, + ): logger.debug(f"Updating server {server_id} backup config with {locals()}") if Backups.select().where(Backups.server_id == server_id).count() != 0: new_row = False @@ -331,34 +367,42 @@ class helpers_management: conf = { "excluded_dirs": None, "max_backups": 0, - "server_id": server_id, - "compress": False + "server_id": server_id, + "compress": False, } new_row = True if max_backups is not None: - conf['max_backups'] = max_backups + conf["max_backups"] = max_backups if excluded_dirs is not None: dirs_to_exclude = ",".join(excluded_dirs) - conf['excluded_dirs'] = dirs_to_exclude - conf['compress'] = compress + conf["excluded_dirs"] = dirs_to_exclude + conf["compress"] = compress if not new_row: with database.atomic(): if backup_path is not None: - u1 = Servers.update(backup_path=backup_path).where(Servers.server_id == server_id).execute() + u1 = ( + Servers.update(backup_path=backup_path) + .where(Servers.server_id == server_id) + .execute() + ) else: u1 = 0 - u2 = Backups.update(conf).where(Backups.server_id == server_id).execute() + u2 = ( + Backups.update(conf).where(Backups.server_id == server_id).execute() + ) logger.debug(f"Updating existing backup record. {u1}+{u2} rows affected") else: with database.atomic(): conf["server_id"] = server_id if backup_path is not None: - Servers.update(backup_path=backup_path).where(Servers.server_id == server_id) + Servers.update(backup_path=backup_path).where( + Servers.server_id == server_id + ) Backups.create(**conf) logger.debug("Creating new backup record.") def get_excluded_backup_dirs(self, server_id: int): - excluded_dirs = self.get_backup_config(server_id)['excluded_dirs'] + excluded_dirs = self.get_backup_config(server_id)["excluded_dirs"] if excluded_dirs is not None and excluded_dirs != "": dir_list = excluded_dirs.split(",") else: @@ -372,7 +416,9 @@ class helpers_management: excluded_dirs = ",".join(dir_list) self.set_backup_config(server_id=server_id, excluded_dirs=excluded_dirs) else: - logger.debug(f"Not adding {dir_to_add} to excluded directories - already in the excluded directory list for server ID {server_id}") + logger.debug( + f"Not adding {dir_to_add} to excluded directories - already in the excluded directory list for server ID {server_id}" + ) def del_excluded_backup_dir(self, server_id: int, dir_to_del: str): dir_list = self.get_excluded_backup_dirs() @@ -381,14 +427,18 @@ class helpers_management: excluded_dirs = ",".join(dir_list) self.set_backup_config(server_id=server_id, excluded_dirs=excluded_dirs) else: - logger.debug(f"Not removing {dir_to_del} from excluded directories - not in the excluded directory list for server ID {server_id}") + logger.debug( + f"Not removing {dir_to_del} from excluded directories - not in the excluded directory list for server ID {server_id}" + ) @staticmethod def clear_unexecuted_commands(): - Commands.update({ - Commands.executed: True - #pylint: disable=singleton-comparison - }).where(Commands.executed == False).execute() + Commands.update( + { + Commands.executed: True + # pylint: disable=singleton-comparison + } + ).where(Commands.executed == False).execute() management_helper = helpers_management() diff --git a/app/classes/models/roles.py b/app/classes/models/roles.py index 782e0af5..6724aafb 100644 --- a/app/classes/models/roles.py +++ b/app/classes/models/roles.py @@ -4,22 +4,29 @@ import datetime from app.classes.shared.helpers import helper try: - from peewee import SqliteDatabase, Model, CharField, DoesNotExist, AutoField, DateTimeField + from peewee import ( + SqliteDatabase, + Model, + CharField, + DoesNotExist, + AutoField, + DateTimeField, + ) from playhouse.shortcuts import model_to_dict except ModuleNotFoundError as e: helper.auto_installer_fix(e) logger = logging.getLogger(__name__) -peewee_logger = logging.getLogger('peewee') +peewee_logger = logging.getLogger("peewee") peewee_logger.setLevel(logging.INFO) -database = SqliteDatabase(helper.db_path, pragmas = { - 'journal_mode': 'wal', - 'cache_size': -1024 * 10}) +database = SqliteDatabase( + helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} +) -#************************************************************************************************ +# ************************************************************************************************ # Roles Class -#************************************************************************************************ +# ************************************************************************************************ class Roles(Model): role_id = AutoField() created = DateTimeField(default=datetime.datetime.now) @@ -30,9 +37,10 @@ class Roles(Model): table_name = "roles" database = database -#************************************************************************************************ + +# ************************************************************************************************ # Roles Helpers -#************************************************************************************************ +# ************************************************************************************************ class helper_roles: @staticmethod def get_all_roles(): @@ -52,10 +60,12 @@ class helper_roles: @staticmethod def add_role(role_name): - role_id = Roles.insert({ - Roles.role_name: role_name.lower(), - Roles.created: helper.get_time_as_string() - }).execute() + role_id = Roles.insert( + { + Roles.role_name: role_name.lower(), + Roles.created: helper.get_time_as_string(), + } + ).execute() return role_id @staticmethod @@ -74,4 +84,5 @@ class helper_roles: return False return True + roles_helper = helper_roles() diff --git a/app/classes/models/server_permissions.py b/app/classes/models/server_permissions.py index 9ec9c0cd..a4024873 100644 --- a/app/classes/models/server_permissions.py +++ b/app/classes/models/server_permissions.py @@ -7,35 +7,43 @@ from app.classes.shared.helpers import helper from app.classes.shared.permission_helper import permission_helper try: - from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, CompositeKey, JOIN + from peewee import ( + SqliteDatabase, + Model, + ForeignKeyField, + CharField, + CompositeKey, + JOIN, + ) from enum import Enum except ModuleNotFoundError as e: helper.auto_installer_fix(e) logger = logging.getLogger(__name__) -peewee_logger = logging.getLogger('peewee') +peewee_logger = logging.getLogger("peewee") peewee_logger.setLevel(logging.INFO) -database = SqliteDatabase(helper.db_path, pragmas = { - 'journal_mode': 'wal', - 'cache_size': -1024 * 10}) +database = SqliteDatabase( + helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} +) -#************************************************************************************************ +# ************************************************************************************************ # Role Servers Class -#************************************************************************************************ +# ************************************************************************************************ class Role_Servers(Model): - role_id = ForeignKeyField(Roles, backref='role_server') - server_id = ForeignKeyField(Servers, backref='role_server') + role_id = ForeignKeyField(Roles, backref="role_server") + server_id = ForeignKeyField(Servers, backref="role_server") permissions = CharField(default="00000000") class Meta: - table_name = 'role_servers' - primary_key = CompositeKey('role_id', 'server_id') + table_name = "role_servers" + primary_key = CompositeKey("role_id", "server_id") database = database -#************************************************************************************************ + +# ************************************************************************************************ # Servers Permissions Class -#************************************************************************************************ +# ************************************************************************************************ class Enum_Permissions_Server(Enum): Commands = 0 Terminal = 1 @@ -46,11 +54,13 @@ class Enum_Permissions_Server(Enum): Config = 6 Players = 7 -class Permissions_Servers: +class Permissions_Servers: @staticmethod def get_or_create(role_id, server, permissions_mask): - return Role_Servers.get_or_create(role_id=role_id, server_id=server, permissions=permissions_mask) + return Role_Servers.get_or_create( + role_id=role_id, server_id=server, permissions=permissions_mask + ) @staticmethod def get_permissions_list(): @@ -69,13 +79,15 @@ class Permissions_Servers: @staticmethod def has_permission(permission_mask, permission_tested: Enum_Permissions_Server): - return permission_mask[permission_tested.value] == '1' + return permission_mask[permission_tested.value] == "1" @staticmethod - def set_permission(permission_mask, permission_tested: Enum_Permissions_Server, value): + def set_permission( + permission_mask, permission_tested: Enum_Permissions_Server, value + ): list_perms = list(permission_mask) list_perms[permission_tested.value] = str(value) - permission_mask = ''.join(list_perms) + permission_mask = "".join(list_perms) return permission_mask @staticmethod @@ -86,50 +98,71 @@ class Permissions_Servers: def get_token_permissions(permissions_mask, api_permissions_mask): permissions_list = [] for member in Enum_Permissions_Server.__members__.items(): - if permission_helper.both_have_perm(permissions_mask, api_permissions_mask, member[1]): + if permission_helper.both_have_perm( + permissions_mask, api_permissions_mask, member[1] + ): permissions_list.append(member[1]) return permissions_list - - #************************************************************************************************ + # ************************************************************************************************ # Role_Servers Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_role_servers_from_role_id(roleid): return Role_Servers.select().where(Role_Servers.role_id == roleid) @staticmethod def get_servers_from_role(role_id): - return Role_Servers.select().join(Servers, JOIN.INNER).where(Role_Servers.role_id == role_id) + return ( + Role_Servers.select() + .join(Servers, JOIN.INNER) + .where(Role_Servers.role_id == role_id) + ) @staticmethod def get_roles_from_server(server_id): - return Role_Servers.select().join(Roles, JOIN.INNER).where(Role_Servers.server_id == server_id) + return ( + Role_Servers.select() + .join(Roles, JOIN.INNER) + .where(Role_Servers.server_id == server_id) + ) @staticmethod def add_role_server(server_id, role_id, rs_permissions="00000000"): - servers = Role_Servers.insert({Role_Servers.server_id: server_id, Role_Servers.role_id: role_id, - Role_Servers.permissions: rs_permissions}).execute() + servers = Role_Servers.insert( + { + Role_Servers.server_id: server_id, + Role_Servers.role_id: role_id, + Role_Servers.permissions: rs_permissions, + } + ).execute() return servers @staticmethod def get_permissions_mask(role_id, server_id): - permissions_mask = '' - role_server = Role_Servers.select().where(Role_Servers.role_id == role_id).where(Role_Servers.server_id == server_id).get() + permissions_mask = "" + role_server = ( + Role_Servers.select() + .where(Role_Servers.role_id == role_id) + .where(Role_Servers.server_id == server_id) + .get() + ) permissions_mask = role_server.permissions return permissions_mask @staticmethod def get_server_roles(server_id): role_list = [] - roles = Role_Servers.select().where(Role_Servers.server_id == server_id).execute() + roles = ( + Role_Servers.select().where(Role_Servers.server_id == server_id).execute() + ) for role in roles: role_list.append(role.role_id) return role_list @staticmethod def get_role_permissions_list(role_id): - permissions_mask = '00000000' + permissions_mask = "00000000" role_server = Role_Servers.get_or_none(Role_Servers.role_id == role_id) if role_server is not None: permissions_mask = role_server.permissions @@ -138,7 +171,12 @@ class Permissions_Servers: @staticmethod def update_role_permission(role_id, server_id, permissions_mask): - role_server = Role_Servers.select().where(Role_Servers.role_id == role_id).where(Role_Servers.server_id == server_id).get() + role_server = ( + Role_Servers.select() + .where(Role_Servers.role_id == role_id) + .where(Role_Servers.server_id == server_id) + .get() + ) role_server.permissions = permissions_mask Role_Servers.save(role_server) @@ -146,12 +184,21 @@ class Permissions_Servers: def delete_roles_permissions(role_id, removed_servers=None): if removed_servers is None: removed_servers = {} - return Role_Servers.delete().where(Role_Servers.role_id == role_id).where(Role_Servers.server_id.in_(removed_servers)).execute() + return ( + Role_Servers.delete() + .where(Role_Servers.role_id == role_id) + .where(Role_Servers.server_id.in_(removed_servers)) + .execute() + ) @staticmethod def remove_roles_of_server(server_id): with database.atomic(): - return Role_Servers.delete().where(Role_Servers.server_id == server_id).execute() + return ( + Role_Servers.delete() + .where(Role_Servers.server_id == server_id) + .execute() + ) @staticmethod def get_user_id_permissions_mask(user_id, server_id: str): @@ -161,14 +208,19 @@ class Permissions_Servers: @staticmethod def get_user_permissions_mask(user: Users, server_id: str): if user.superuser: - permissions_mask = '1' * len(server_permissions.get_permissions_list()) + permissions_mask = "1" * len(server_permissions.get_permissions_list()) else: roles_list = users_helper.get_user_roles_id(user.user_id) - role_server = Role_Servers.select().where(Role_Servers.role_id.in_(roles_list)).where(Role_Servers.server_id == server_id).execute() + role_server = ( + Role_Servers.select() + .where(Role_Servers.role_id.in_(roles_list)) + .where(Role_Servers.server_id == server_id) + .execute() + ) try: permissions_mask = role_server[0].permissions except IndexError: - permissions_mask = '0' * len(server_permissions.get_permissions_list()) + permissions_mask = "0" * len(server_permissions.get_permissions_list()) return permissions_mask @staticmethod @@ -197,7 +249,9 @@ class Permissions_Servers: if user.superuser: permissions_list = server_permissions.get_permissions_list() else: - permissions_mask = server_permissions.get_user_permissions_mask(user, server_id) + permissions_mask = server_permissions.get_user_permissions_mask( + user, server_id + ) permissions_list = server_permissions.get_permissions(permissions_mask) return permissions_list @@ -212,11 +266,18 @@ class Permissions_Servers: if user.superuser and key.superuser: return server_permissions.get_permissions_list() else: - roles_list = users_helper.get_user_roles_id(user['user_id']) - role_server = Role_Servers.select().where(Role_Servers.role_id.in_(roles_list)).where(Role_Servers.server_id == server_id).execute() + roles_list = users_helper.get_user_roles_id(user["user_id"]) + role_server = ( + Role_Servers.select() + .where(Role_Servers.role_id.in_(roles_list)) + .where(Role_Servers.server_id == server_id) + .execute() + ) user_permissions_mask = role_server[0].permissions key_permissions_mask = key.server_permissions - permissions_mask = permission_helper.combine_masks(user_permissions_mask, key_permissions_mask) + permissions_mask = permission_helper.combine_masks( + user_permissions_mask, key_permissions_mask + ) permissions_list = server_permissions.get_permissions(permissions_mask) return permissions_list diff --git a/app/classes/models/servers.py b/app/classes/models/servers.py index 07b87d31..8d5526ec 100644 --- a/app/classes/models/servers.py +++ b/app/classes/models/servers.py @@ -5,21 +5,31 @@ from app.classes.shared.helpers import helper from app.classes.shared.main_models import db_helper try: - from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, AutoField, DateTimeField, BooleanField, IntegerField, FloatField + from peewee import ( + SqliteDatabase, + Model, + ForeignKeyField, + CharField, + AutoField, + DateTimeField, + BooleanField, + IntegerField, + FloatField, + ) except ModuleNotFoundError as e: helper.auto_installer_fix(e) logger = logging.getLogger(__name__) -peewee_logger = logging.getLogger('peewee') +peewee_logger = logging.getLogger("peewee") peewee_logger.setLevel(logging.INFO) -database = SqliteDatabase(helper.db_path, pragmas = { - 'journal_mode': 'wal', - 'cache_size': -1024 * 10}) +database = SqliteDatabase( + helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} +) -#************************************************************************************************ +# ************************************************************************************************ # Servers Class -#************************************************************************************************ +# ************************************************************************************************ class Servers(Model): server_id = AutoField() created = DateTimeField(default=datetime.datetime.now) @@ -45,13 +55,13 @@ class Servers(Model): database = database -#************************************************************************************************ +# ************************************************************************************************ # Servers Stats Class -#************************************************************************************************ +# ************************************************************************************************ class Server_Stats(Model): stats_id = AutoField() created = DateTimeField(default=datetime.datetime.now) - server_id = ForeignKeyField(Servers, backref='server', index=True) + server_id = ForeignKeyField(Servers, backref="server", index=True) started = CharField(default="") running = BooleanField(default=False) cpu = FloatField(default=0) @@ -72,20 +82,19 @@ class Server_Stats(Model): crashed = BooleanField(default=False) downloading = BooleanField(default=False) - class Meta: table_name = "server_stats" database = database -#************************************************************************************************ +# ************************************************************************************************ # Servers Class -#************************************************************************************************ +# ************************************************************************************************ class helper_servers: - #************************************************************************************************ + # ************************************************************************************************ # Generic Servers Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def create_server( name: str, @@ -97,23 +106,25 @@ class helper_servers: server_log_file: str, server_stop: str, server_type: str, - server_port=25565): - return Servers.insert({ - Servers.server_name: name, - Servers.server_uuid: server_uuid, - Servers.path: server_dir, - Servers.executable: server_file, - Servers.execution_command: server_command, - Servers.auto_start: False, - Servers.auto_start_delay: 10, - Servers.crash_detection: False, - Servers.log_path: server_log_file, - Servers.server_port: server_port, - Servers.stop_command: server_stop, - Servers.backup_path: backup_path, - Servers.type: server_type - }).execute() - + server_port=25565, + ): + return Servers.insert( + { + Servers.server_name: name, + Servers.server_uuid: server_uuid, + Servers.path: server_dir, + Servers.executable: server_file, + Servers.execution_command: server_command, + Servers.auto_start: False, + Servers.auto_start_delay: 10, + Servers.crash_detection: False, + Servers.log_path: server_log_file, + Servers.server_port: server_port, + Servers.stop_command: server_stop, + Servers.backup_path: backup_path, + Servers.type: server_type, + } + ).execute() @staticmethod def get_server_obj(server_id): @@ -141,9 +152,9 @@ class helper_servers: except IndexError: return {} - #************************************************************************************************ + # ************************************************************************************************ # Servers Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_all_defined_servers(): query = Servers.select() @@ -155,10 +166,23 @@ class helper_servers: server_data = [] try: for s in servers: - latest = Server_Stats.select().where(Server_Stats.server_id == s.get('server_id')).order_by(Server_Stats.created.desc()).limit(1) - server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)[0], "user_command_permission":True}) + latest = ( + Server_Stats.select() + .where(Server_Stats.server_id == s.get("server_id")) + .order_by(Server_Stats.created.desc()) + .limit(1) + ) + server_data.append( + { + "server_data": s, + "stats": db_helper.return_rows(latest)[0], + "user_command_permission": True, + } + ) except IndexError as ex: - logger.error(f"Stats collection failed with error: {ex}. Was a server just created?") + logger.error( + f"Stats collection failed with error: {ex}. Was a server just created?" + ) return server_data @staticmethod @@ -167,16 +191,26 @@ class helper_servers: friendly_name = f"{server_data.get('server_name', None)} with ID: {server_data.get('server_id', 0)}" return friendly_name - #************************************************************************************************ + # ************************************************************************************************ # Servers_Stats Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def get_latest_server_stats(server_id): - return Server_Stats.select().where(Server_Stats.server_id == server_id).order_by(Server_Stats.created.desc()).limit(1) + return ( + Server_Stats.select() + .where(Server_Stats.server_id == server_id) + .order_by(Server_Stats.created.desc()) + .limit(1) + ) @staticmethod def get_server_stats_by_id(server_id): - stats = Server_Stats.select().where(Server_Stats.server_id == server_id).order_by(Server_Stats.created.desc()).limit(1) + stats = ( + Server_Stats.select() + .where(Server_Stats.server_id == server_id) + .order_by(Server_Stats.created.desc()) + .limit(1) + ) return db_helper.return_rows(stats)[0] @staticmethod @@ -188,33 +222,42 @@ class helper_servers: @staticmethod def sever_crashed(server_id): with database.atomic(): - Server_Stats.update(crashed=True).where(Server_Stats.server_id == server_id).execute() + Server_Stats.update(crashed=True).where( + Server_Stats.server_id == server_id + ).execute() @staticmethod def set_download(server_id): with database.atomic(): - Server_Stats.update(downloading=True).where(Server_Stats.server_id == server_id).execute() + Server_Stats.update(downloading=True).where( + Server_Stats.server_id == server_id + ).execute() @staticmethod def finish_download(server_id): with database.atomic(): - Server_Stats.update(downloading=False).where(Server_Stats.server_id == server_id).execute() + Server_Stats.update(downloading=False).where( + Server_Stats.server_id == server_id + ).execute() @staticmethod def get_download_status(server_id): - download_status = Server_Stats.select().where(Server_Stats.server_id == server_id).get() + download_status = ( + Server_Stats.select().where(Server_Stats.server_id == server_id).get() + ) return download_status.downloading - @staticmethod def server_crash_reset(server_id): with database.atomic(): - Server_Stats.update(crashed=False).where(Server_Stats.server_id == server_id).execute() + Server_Stats.update(crashed=False).where( + Server_Stats.server_id == server_id + ).execute() @staticmethod def is_crashed(server_id): svr = Server_Stats.select().where(Server_Stats.server_id == server_id).get() - #pylint: disable=singleton-comparison + # pylint: disable=singleton-comparison if svr.crashed == True: return True else: @@ -223,44 +266,58 @@ class helper_servers: @staticmethod def set_update(server_id, value): try: - #Checks if server even exists + # Checks if server even exists Server_Stats.select().where(Server_Stats.server_id == server_id) except Exception as ex: logger.error(f"Database entry not found! {ex}") with database.atomic(): - Server_Stats.update(updating=value).where(Server_Stats.server_id == server_id).execute() + Server_Stats.update(updating=value).where( + Server_Stats.server_id == server_id + ).execute() @staticmethod def get_update_status(server_id): - update_status = Server_Stats.select().where(Server_Stats.server_id == server_id).get() + update_status = ( + Server_Stats.select().where(Server_Stats.server_id == server_id).get() + ) return update_status.updating @staticmethod def set_first_run(server_id): - #Sets first run to false + # Sets first run to false try: - #Checks if server even exists + # Checks if server even exists Server_Stats.select().where(Server_Stats.server_id == server_id) except Exception as ex: logger.error(f"Database entry not found! {ex}") return with database.atomic(): - Server_Stats.update(first_run=False).where(Server_Stats.server_id == server_id).execute() + Server_Stats.update(first_run=False).where( + Server_Stats.server_id == server_id + ).execute() @staticmethod def get_first_run(server_id): - first_run = Server_Stats.select().where(Server_Stats.server_id == server_id).get() + first_run = ( + Server_Stats.select().where(Server_Stats.server_id == server_id).get() + ) return first_run.first_run @staticmethod def get_TTL_without_player(server_id): - last_stat = Server_Stats.select().where(Server_Stats.server_id == server_id).order_by(Server_Stats.created.desc()).first() - last_stat_with_player = (Server_Stats - .select() - .where(Server_Stats.server_id == server_id) - .where(Server_Stats.online > 0) - .order_by(Server_Stats.created.desc()) - .first()) + last_stat = ( + Server_Stats.select() + .where(Server_Stats.server_id == server_id) + .order_by(Server_Stats.created.desc()) + .first() + ) + last_stat_with_player = ( + Server_Stats.select() + .where(Server_Stats.server_id == server_id) + .where(Server_Stats.online > 0) + .order_by(Server_Stats.created.desc()) + .first() + ) return last_stat.created - last_stat_with_player.created @staticmethod @@ -279,11 +336,15 @@ class helper_servers: except Exception as ex: logger.error(f"Database entry not found! {ex}") with database.atomic(): - Server_Stats.update(waiting_start=value).where(Server_Stats.server_id == server_id).execute() + Server_Stats.update(waiting_start=value).where( + Server_Stats.server_id == server_id + ).execute() @staticmethod def get_waiting_start(server_id): - waiting_start = Server_Stats.select().where(Server_Stats.server_id == server_id).get() + waiting_start = ( + Server_Stats.select().where(Server_Stats.server_id == server_id).get() + ) return waiting_start.waiting_start diff --git a/app/classes/models/users.py b/app/classes/models/users.py index 751fdb1c..24020e61 100644 --- a/app/classes/models/users.py +++ b/app/classes/models/users.py @@ -6,22 +6,33 @@ from app.classes.models.roles import Roles, roles_helper from app.classes.shared.helpers import helper try: - from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, AutoField, DateTimeField, BooleanField, CompositeKey, DoesNotExist, JOIN + from peewee import ( + SqliteDatabase, + Model, + ForeignKeyField, + CharField, + AutoField, + DateTimeField, + BooleanField, + CompositeKey, + DoesNotExist, + JOIN, + ) from playhouse.shortcuts import model_to_dict except ModuleNotFoundError as e: helper.auto_installer_fix(e) logger = logging.getLogger(__name__) -peewee_logger = logging.getLogger('peewee') +peewee_logger = logging.getLogger("peewee") peewee_logger.setLevel(logging.INFO) -database = SqliteDatabase(helper.db_path, pragmas = { - 'journal_mode': 'wal', - 'cache_size': -1024 * 10}) +database = SqliteDatabase( + helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} +) -#************************************************************************************************ +# ************************************************************************************************ # Users Class -#************************************************************************************************ +# ************************************************************************************************ class Users(Model): user_id = AutoField() created = DateTimeField(default=datetime.datetime.now) @@ -34,7 +45,7 @@ class Users(Model): enabled = BooleanField(default=True) superuser = BooleanField(default=False) lang = CharField(default="en_EN") - support_logs = CharField(default = '') + support_logs = CharField(default="") valid_tokens_from = DateTimeField(default=datetime.datetime.now) server_order = CharField(default="") preparing = BooleanField(default=False) @@ -49,35 +60,35 @@ class Users(Model): # ************************************************************************************************ class ApiKeys(Model): token_id = AutoField() - name = CharField(default='', unique=True, index=True) + name = CharField(default="", unique=True, index=True) created = DateTimeField(default=datetime.datetime.now) - user_id = ForeignKeyField(Users, backref='api_token', index=True) - server_permissions = CharField(default='00000000') - crafty_permissions = CharField(default='000') + user_id = ForeignKeyField(Users, backref="api_token", index=True) + server_permissions = CharField(default="00000000") + crafty_permissions = CharField(default="000") superuser = BooleanField(default=False) class Meta: - table_name = 'api_keys' + table_name = "api_keys" database = database -#************************************************************************************************ +# ************************************************************************************************ # User Roles Class -#************************************************************************************************ +# ************************************************************************************************ class User_Roles(Model): - user_id = ForeignKeyField(Users, backref='user_role') - role_id = ForeignKeyField(Roles, backref='user_role') + user_id = ForeignKeyField(Users, backref="user_role") + role_id = ForeignKeyField(Roles, backref="user_role") class Meta: - table_name = 'user_roles' - primary_key = CompositeKey('user_id', 'role_id') + table_name = "user_roles" + primary_key = CompositeKey("user_id", "role_id") database = database -#************************************************************************************************ -# Users Helpers -#************************************************************************************************ -class helper_users: +# ************************************************************************************************ +# Users Helpers +# ************************************************************************************************ +class helper_users: @staticmethod def get_by_id(user_id): return Users.get_by_id(user_id) @@ -107,19 +118,19 @@ class helper_users: def get_user(user_id): if user_id == 0: return { - 'user_id': 0, - 'created': '10/24/2019, 11:34:00', - 'last_login': '10/24/2019, 11:34:00', - 'last_update': '10/24/2019, 11:34:00', - 'last_ip': "127.27.23.89", - 'username': "SYSTEM", - 'password': None, - 'email': "default@example.com", - 'enabled': True, - 'superuser': True, - 'roles': [], - 'servers': [], - 'support_logs': '', + "user_id": 0, + "created": "10/24/2019, 11:34:00", + "last_login": "10/24/2019, 11:34:00", + "last_update": "10/24/2019, 11:34:00", + "last_ip": "127.27.23.89", + "username": "SYSTEM", + "password": None, + "email": "default@example.com", + "enabled": True, + "superuser": True, + "roles": [], + "servers": [], + "support_logs": "", } user = model_to_dict(Users.get(Users.user_id == user_id)) @@ -128,7 +139,7 @@ class helper_users: user = users_helper.add_user_roles(user) return user else: - #logger.debug("user: ({}) {}".format(user_id, {})) + # logger.debug("user: ({}) {}".format(user_id, {})) return {} @staticmethod @@ -147,31 +158,47 @@ class helper_users: return user @staticmethod - def add_user(username: str, password: str = None, email: Optional[str] = None, enabled: bool = True, superuser: bool = False) -> str: + def add_user( + username: str, + password: str = None, + email: Optional[str] = None, + enabled: bool = True, + superuser: bool = False, + ) -> str: if password is not None: pw_enc = helper.encode_pass(password) else: pw_enc = None - user_id = Users.insert({ - Users.username: username.lower(), - Users.password: pw_enc, - Users.email: email, - Users.enabled: enabled, - Users.superuser: superuser, - Users.created: helper.get_time_as_string() - }).execute() + user_id = Users.insert( + { + Users.username: username.lower(), + Users.password: pw_enc, + Users.email: email, + Users.enabled: enabled, + Users.superuser: superuser, + Users.created: helper.get_time_as_string(), + } + ).execute() return user_id @staticmethod - def add_rawpass_user(username: str, password: str = None, email: Optional[str] = None, enabled: bool = True, superuser: bool = False) -> str: - user_id = Users.insert({ - Users.username: username.lower(), - Users.password: password, - Users.email: email, - Users.enabled: enabled, - Users.superuser: superuser, - Users.created: helper.get_time_as_string() - }).execute() + def add_rawpass_user( + username: str, + password: str = None, + email: Optional[str] = None, + enabled: bool = True, + superuser: bool = False, + ) -> str: + user_id = Users.insert( + { + Users.username: username.lower(), + Users.password: password, + Users.email: email, + Users.enabled: enabled, + Users.superuser: superuser, + Users.created: helper.get_time_as_string(), + } + ).execute() return user_id @staticmethod @@ -183,7 +210,9 @@ class helper_users: @staticmethod def update_server_order(user_id, user_server_order): - Users.update(server_order = user_server_order).where(Users.user_id == user_id).execute() + Users.update(server_order=user_server_order).where( + Users.user_id == user_id + ).execute() @staticmethod def get_server_order(user_id): @@ -208,20 +237,22 @@ class helper_users: @staticmethod def set_support_path(user_id, support_path): - Users.update(support_logs = support_path).where(Users.user_id == user_id).execute() + Users.update(support_logs=support_path).where( + Users.user_id == user_id + ).execute() @staticmethod def set_prepare(user_id): - Users.update(preparing = True).where(Users.user_id == user_id).execute() + Users.update(preparing=True).where(Users.user_id == user_id).execute() @staticmethod def stop_prepare(user_id): - Users.update(preparing = False).where(Users.user_id == user_id).execute() + Users.update(preparing=False).where(Users.user_id == user_id).execute() @staticmethod def clear_support_status(): - #pylint: disable=singleton-comparison - Users.update(preparing = False).where(Users.preparing == True).execute() + # pylint: disable=singleton-comparison + Users.update(preparing=False).where(Users.preparing == True).execute() @staticmethod def user_id_exists(user_id): @@ -229,9 +260,9 @@ class helper_users: return False return True -#************************************************************************************************ -# User_Roles Methods -#************************************************************************************************ + # ************************************************************************************************ + # User_Roles Methods + # ************************************************************************************************ @staticmethod def get_or_create(user_id, role_id): @@ -242,7 +273,7 @@ class helper_users: roles_list = [] roles = User_Roles.select().where(User_Roles.user_id == user_id) for r in roles: - roles_list.append(roles_helper.get_role(r.role_id)['role_id']) + roles_list.append(roles_helper.get_role(r.role_id)["role_id"]) return roles_list @staticmethod @@ -250,37 +281,40 @@ class helper_users: roles_list = [] roles = User_Roles.select().where(User_Roles.user_id == user_id) for r in roles: - roles_list.append(roles_helper.get_role(r.role_id)['role_name']) + roles_list.append(roles_helper.get_role(r.role_id)["role_name"]) return roles_list @staticmethod def add_role_to_user(user_id, role_id): - User_Roles.insert({ - User_Roles.user_id: user_id, - User_Roles.role_id: role_id - }).execute() + User_Roles.insert( + {User_Roles.user_id: user_id, User_Roles.role_id: role_id} + ).execute() @staticmethod def add_user_roles(user: Union[dict, Users]): if isinstance(user, dict): - user_id = user['user_id'] + user_id = user["user_id"] else: user_id = user.user_id # I just copied this code from get_user, it had those TODOs & comments made by mac - Lukas - roles_query = User_Roles.select().join(Roles, JOIN.INNER).where(User_Roles.user_id == user_id) + roles_query = ( + User_Roles.select() + .join(Roles, JOIN.INNER) + .where(User_Roles.user_id == user_id) + ) # TODO: this query needs to be narrower roles = set() for r in roles_query: roles.add(r.role_id.role_id) if isinstance(user, dict): - user['roles'] = roles + user["roles"] = roles else: user.roles = roles - #logger.debug("user: ({}) {}".format(user_id, user)) + # logger.debug("user: ({}) {}".format(user_id, user)) return user @staticmethod @@ -293,15 +327,17 @@ class helper_users: @staticmethod def delete_user_roles(user_id, removed_roles): - User_Roles.delete().where(User_Roles.user_id == user_id).where(User_Roles.role_id.in_(removed_roles)).execute() + User_Roles.delete().where(User_Roles.user_id == user_id).where( + User_Roles.role_id.in_(removed_roles) + ).execute() @staticmethod def remove_roles_from_role_id(role_id): User_Roles.delete().where(User_Roles.role_id == role_id).execute() -# ************************************************************************************************ -# ApiKeys Methods -# ************************************************************************************************ + # ************************************************************************************************ + # ApiKeys Methods + # ************************************************************************************************ @staticmethod def get_user_api_keys(user_id: str): @@ -313,18 +349,29 @@ class helper_users: @staticmethod def add_user_api_key( - name: str, - user_id: str, - superuser: bool = False, - server_permissions_mask: Optional[str] = None, - crafty_permissions_mask: Optional[str] = None): - return ApiKeys.insert({ - ApiKeys.name: name, - ApiKeys.user_id: user_id, - **({ApiKeys.server_permissions: server_permissions_mask} if server_permissions_mask is not None else {}), - **({ApiKeys.crafty_permissions: crafty_permissions_mask} if crafty_permissions_mask is not None else {}), - ApiKeys.superuser: superuser - }).execute() + name: str, + user_id: str, + superuser: bool = False, + server_permissions_mask: Optional[str] = None, + crafty_permissions_mask: Optional[str] = None, + ): + return ApiKeys.insert( + { + ApiKeys.name: name, + ApiKeys.user_id: user_id, + **( + {ApiKeys.server_permissions: server_permissions_mask} + if server_permissions_mask is not None + else {} + ), + **( + {ApiKeys.crafty_permissions: crafty_permissions_mask} + if crafty_permissions_mask is not None + else {} + ), + ApiKeys.superuser: superuser, + } + ).execute() @staticmethod def delete_user_api_keys(user_id: str): @@ -335,5 +382,4 @@ class helper_users: ApiKeys.delete().where(ApiKeys.token_id == key_id).execute() - users_helper = helper_users() diff --git a/app/classes/shared/authentication.py b/app/classes/shared/authentication.py index 25053d0b..6592fdd3 100644 --- a/app/classes/shared/authentication.py +++ b/app/classes/shared/authentication.py @@ -14,12 +14,13 @@ except ModuleNotFoundError as e: logger = logging.getLogger(__name__) + class Authentication: def __init__(self): self.secret = "my secret" - self.secret = helper.get_setting('apikey_secret', None) + self.secret = helper.get_setting("apikey_secret", None) - if self.secret is None or self.secret == 'random': + if self.secret is None or self.secret == "random": self.secret = helper.random_string_generator(64) @staticmethod @@ -27,13 +28,9 @@ class Authentication: if extra is None: extra = {} return jwt.encode( - { - 'user_id': user_id, - 'iat': int(time.time()), - **extra - }, + {"user_id": user_id, "iat": int(time.time()), **extra}, authentication.secret, - algorithm="HS256" + algorithm="HS256", ) @staticmethod @@ -49,23 +46,25 @@ class Authentication: return None @staticmethod - def check(token) -> Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]: + def check( + token, + ) -> Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]: try: data = jwt.decode(token, authentication.secret, algorithms=["HS256"]) except PyJWTError as error: logger.debug("Error while checking JWT token: ", exc_info=error) return None - iat: int = data['iat'] + iat: int = data["iat"] key: Optional[ApiKeys] = None - if 'token_id' in data: - key_id = data['token_id'] + if "token_id" in data: + key_id = data["token_id"] key = users_helper.get_user_api_key(key_id) if key is None: return None - user_id: str = data['user_id'] + user_id: str = data["user_id"] user = users_helper.get_user(user_id) # TODO: Have a cache or something so we don't constantly have to query the database - if int(user.get('valid_tokens_from').timestamp()) < iat: + if int(user.get("valid_tokens_from").timestamp()) < iat: # Success! return key, data, user else: diff --git a/app/classes/shared/command.py b/app/classes/shared/command.py index 112effbb..8907f4c5 100644 --- a/app/classes/shared/command.py +++ b/app/classes/shared/command.py @@ -11,8 +11,8 @@ from app.classes.web.websocket_helper import websocket_helper logger = logging.getLogger(__name__) -class MainPrompt(cmd.Cmd): +class MainPrompt(cmd.Cmd): def __init__(self, tasks_manager, migration_manager): super().__init__() self.tasks_manager = tasks_manager @@ -25,48 +25,52 @@ class MainPrompt(cmd.Cmd): def emptyline(): pass - #pylint: disable=unused-argument + # pylint: disable=unused-argument def do_exit(self, line): self.tasks_manager._main_graceful_exit() self.universal_exit() def do_migrations(self, line): - if line == 'up': + if line == "up": self.migration_manager.up() - elif line == 'down': + elif line == "down": self.migration_manager.down() - elif line == 'done': + elif line == "done": console.info(self.migration_manager.done) - elif line == 'todo': + elif line == "todo": console.info(self.migration_manager.todo) - elif line == 'diff': + elif line == "diff": console.info(self.migration_manager.diff) - elif line == 'info': - console.info(f'Done: {self.migration_manager.done}') - console.info(f'FS: {self.migration_manager.todo}') - console.info(f'Todo: {self.migration_manager.diff}') - elif line.startswith('add '): - migration_name = line[len('add '):] + elif line == "info": + console.info(f"Done: {self.migration_manager.done}") + console.info(f"FS: {self.migration_manager.todo}") + console.info(f"Todo: {self.migration_manager.diff}") + elif line.startswith("add "): + migration_name = line[len("add ") :] self.migration_manager.create(migration_name, False) else: - console.info('Unknown migration command') + console.info("Unknown migration command") @staticmethod def do_threads(_line): for thread in threading.enumerate(): if sys.version_info >= (3, 8): - print(f'Name: {thread.name} Identifier: {thread.ident} TID/PID: {thread.native_id}') + print( + f"Name: {thread.name} Identifier: {thread.ident} TID/PID: {thread.native_id}" + ) else: - print(f'Name: {thread.name} Identifier: {thread.ident}') + print(f"Name: {thread.name} Identifier: {thread.ident}") def do_import3(self, _line): import3.start_import() def universal_exit(self): logger.info("Stopping all server daemons / threads") - console.info("Stopping all server daemons / threads - This may take a few seconds") + console.info( + "Stopping all server daemons / threads - This may take a few seconds" + ) websocket_helper.disconnect_all() - console.info('Waiting for main thread to stop') + console.info("Waiting for main thread to stop") while True: if self.tasks_manager.get_main_thread_run_status(): sys.exit(0) diff --git a/app/classes/shared/console.py b/app/classes/shared/console.py index 96b1e324..bcaf17a6 100644 --- a/app/classes/shared/console.py +++ b/app/classes/shared/console.py @@ -12,16 +12,18 @@ except ModuleNotFoundError as ex: logger.critical(f"Import Error: Unable to load {ex.name} module", exc_info=True) print(f"Import Error: Unable to load {ex.name} module") from app.classes.shared.installer import installer - installer.do_install() -class Console: + installer.do_install() + + +class Console: def __init__(self): - if 'colorama' in sys.modules: + if "colorama" in sys.modules: init() @staticmethod def do_print(message, color): - if 'termcolor' in sys.modules or 'colorama' in sys.modules: + if "termcolor" in sys.modules or "colorama" in sys.modules: print(colored(message, color)) else: print(message) diff --git a/app/classes/shared/exceptions.py b/app/classes/shared/exceptions.py index 05a46fdb..0b1cac75 100644 --- a/app/classes/shared/exceptions.py +++ b/app/classes/shared/exceptions.py @@ -1,8 +1,10 @@ class CraftyException(Exception): pass + class DatabaseException(CraftyException): pass + class SchemaError(DatabaseException): pass diff --git a/app/classes/shared/file_helpers.py b/app/classes/shared/file_helpers.py index 205c9066..a706d115 100644 --- a/app/classes/shared/file_helpers.py +++ b/app/classes/shared/file_helpers.py @@ -6,12 +6,9 @@ from zipfile import ZipFile, ZIP_DEFLATED logger = logging.getLogger(__name__) + class FileHelpers: - allowed_quotes = [ - "\"", - "'", - "`" - ] + allowed_quotes = ['"', "'", "`"] def del_dirs(self, path): path = pathlib.Path(path) @@ -32,7 +29,7 @@ class FileHelpers: path = pathlib.Path(path) try: logger.debug(f"Deleting file: {path}") - #Remove the file + # Remove the file os.remove(path) return True except FileNotFoundError: @@ -59,43 +56,58 @@ class FileHelpers: @staticmethod def make_archive(path_to_destination, path_to_zip): # create a ZipFile object - path_to_destination += '.zip' - with ZipFile(path_to_destination, 'w') as z: + path_to_destination += ".zip" + with ZipFile(path_to_destination, "w") as z: for root, _dirs, files in os.walk(path_to_zip, topdown=True): ziproot = path_to_zip for file in files: try: logger.info(f"backing up: {os.path.join(root, file)}") if os.name == "nt": - z.write(os.path.join(root, file), os.path.join(root.replace(ziproot, ""), file)) + z.write( + os.path.join(root, file), + os.path.join(root.replace(ziproot, ""), file), + ) else: - z.write(os.path.join(root, file), os.path.join(root.replace(ziproot, "/"), file)) + z.write( + os.path.join(root, file), + os.path.join(root.replace(ziproot, "/"), file), + ) except Exception as e: - logger.warning(f"Error backing up: {os.path.join(root, file)}! - Error was: {e}") - + logger.warning( + f"Error backing up: {os.path.join(root, file)}! - Error was: {e}" + ) return True @staticmethod def make_compressed_archive(path_to_destination, path_to_zip): # create a ZipFile object - path_to_destination += '.zip' - with ZipFile(path_to_destination, 'w', ZIP_DEFLATED) as z: + path_to_destination += ".zip" + with ZipFile(path_to_destination, "w", ZIP_DEFLATED) as z: for root, _dirs, files in os.walk(path_to_zip, topdown=True): ziproot = path_to_zip for file in files: try: logger.info(f"backing up: {os.path.join(root, file)}") if os.name == "nt": - z.write(os.path.join(root, file), os.path.join(root.replace(ziproot, ""), file)) + z.write( + os.path.join(root, file), + os.path.join(root.replace(ziproot, ""), file), + ) else: - z.write(os.path.join(root, file), os.path.join(root.replace(ziproot, "/"), file)) + z.write( + os.path.join(root, file), + os.path.join(root.replace(ziproot, "/"), file), + ) except Exception as e: - logger.warning(f"Error backing up: {os.path.join(root, file)}! - Error was: {e}") - + logger.warning( + f"Error backing up: {os.path.join(root, file)}! - Error was: {e}" + ) return True + file_helper = FileHelpers() diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 4af17478..cbc75b2a 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -37,28 +37,27 @@ except ModuleNotFoundError as err: print(f"Import Error: Unable to load {err.name} module") installer.do_install() + class Helpers: - allowed_quotes = [ - "\"", - "'", - "`" - ] + allowed_quotes = ['"', "'", "`"] def __init__(self): self.root_dir = os.path.abspath(os.path.curdir) - self.config_dir = os.path.join(self.root_dir, 'app', 'config') - self.webroot = os.path.join(self.root_dir, 'app', 'frontend') - self.servers_dir = os.path.join(self.root_dir, 'servers') - self.backup_path = os.path.join(self.root_dir, 'backups') - self.migration_dir = os.path.join(self.root_dir, 'app', 'migrations') + self.config_dir = os.path.join(self.root_dir, "app", "config") + self.webroot = os.path.join(self.root_dir, "app", "frontend") + self.servers_dir = os.path.join(self.root_dir, "servers") + self.backup_path = os.path.join(self.root_dir, "backups") + self.migration_dir = os.path.join(self.root_dir, "app", "migrations") - self.session_file = os.path.join(self.root_dir, 'app', 'config', 'session.lock') - self.settings_file = os.path.join(self.root_dir, 'app', 'config', 'config.json') + self.session_file = os.path.join(self.root_dir, "app", "config", "session.lock") + self.settings_file = os.path.join(self.root_dir, "app", "config", "config.json") - self.ensure_dir_exists(os.path.join(self.root_dir, 'app', 'config', 'db')) - self.db_path = os.path.join(self.root_dir, 'app', 'config', 'db', 'crafty.sqlite') - self.serverjar_cache = os.path.join(self.config_dir, 'serverjars.json') - self.credits_cache = os.path.join(self.config_dir, 'credits.json') + self.ensure_dir_exists(os.path.join(self.root_dir, "app", "config", "db")) + self.db_path = os.path.join( + self.root_dir, "app", "config", "db", "crafty.sqlite" + ) + self.serverjar_cache = os.path.join(self.config_dir, "serverjars.json") + self.credits_cache = os.path.join(self.config_dir, "credits.json") self.passhasher = PasswordHasher() self.exiting = False @@ -74,7 +73,7 @@ class Helpers: def check_file_perms(self, path): try: - open(path, "r", encoding='utf-8').close() + open(path, "r", encoding="utf-8").close() logger.info(f"{path} is readable") return True except PermissionError: @@ -97,7 +96,7 @@ class Helpers: @staticmethod def check_internet(): try: - requests.get('https://google.com', timeout=1) + requests.get("https://google.com", timeout=1) return True except Exception: return False @@ -105,9 +104,9 @@ class Helpers: @staticmethod def check_port(server_port): try: - ip = get('https://api.ipify.org').content.decode('utf8') + ip = get("https://api.ipify.org").content.decode("utf8") except: - ip = 'google.com' + ip = "google.com" a_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) a_socket.settimeout(20.0) @@ -125,7 +124,7 @@ class Helpers: def check_server_conn(server_port): a_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) a_socket.settimeout(10.0) - ip = '127.0.0.1' + ip = "127.0.0.1" location = (ip, server_port) result_of_check = a_socket.connect_ex(location) @@ -139,43 +138,55 @@ class Helpers: @staticmethod def cmdparse(cmd_in): # Parse a string into arguments - cmd_out = [] # "argv" output array - ci = -1 # command index - pointer to the argument we're building in cmd_out - np = True # whether we're creating a new argument/parameter + cmd_out = [] # "argv" output array + ci = -1 # command index - pointer to the argument we're building in cmd_out + np = True # whether we're creating a new argument/parameter esc = False # whether an escape character was encountered stch = None # if we're dealing with a quote, save the quote type here. Nested quotes to be dealt with by the command - for c in cmd_in: # for character in string - if np: # if set, begin a new argument and increment the command index. Continue the loop. - if c == ' ': + for c in cmd_in: # for character in string + if ( + np + ): # if set, begin a new argument and increment the command index. Continue the loop. + if c == " ": continue else: ci += 1 cmd_out.append("") np = False - if esc: # if we encountered an escape character on the last loop, append this char regardless of what it is + if ( + esc + ): # if we encountered an escape character on the last loop, append this char regardless of what it is if c not in Helpers.allowed_quotes: - cmd_out[ci] += '\\' + cmd_out[ci] += "\\" cmd_out[ci] += c esc = False else: - if c == '\\': # if the current character is an escape character, set the esc flag and continue to next loop + if ( + c == "\\" + ): # if the current character is an escape character, set the esc flag and continue to next loop esc = True - elif c == ' ' and stch is None: # if we encounter a space and are not dealing with a quote, - # set the new argument flag and continue to next loop + elif ( + c == " " and stch is None + ): # if we encounter a space and are not dealing with a quote, + # set the new argument flag and continue to next loop np = True - elif c == stch: # if we encounter the character that matches our start quote, end the quote and continue to next loop + elif ( + c == stch + ): # if we encounter the character that matches our start quote, end the quote and continue to next loop stch = None - elif stch is None and (c in Helpers.allowed_quotes): # if we're not in the middle of a quote and we get a quotable character, - # start a quote and proceed to the next loop + elif stch is None and ( + c in Helpers.allowed_quotes + ): # if we're not in the middle of a quote and we get a quotable character, + # start a quote and proceed to the next loop stch = c - else: # else, just store the character in the current arg + else: # else, just store the character in the current arg cmd_out[ci] += c return cmd_out def get_setting(self, key, default_return=False): try: - with open(self.settings_file, "r", encoding='utf-8') as f: + with open(self.settings_file, "r", encoding="utf-8") as f: data = json.load(f) if key in data.keys(): @@ -187,8 +198,12 @@ class Helpers: return default_return except Exception as e: - logger.critical(f"Config File Error: Unable to read {self.settings_file} due to {e}") - console.critical(f"Config File Error: Unable to read {self.settings_file} due to {e}") + logger.critical( + f"Config File Error: Unable to read {self.settings_file} due to {e}" + ) + console.critical( + f"Config File Error: Unable to read {self.settings_file} due to {e}" + ) return default_return @@ -196,10 +211,10 @@ class Helpers: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: # doesn't even have to be reachable - s.connect(('10.255.255.255', 1)) + s.connect(("10.255.255.255", 1)) IP = s.getsockname()[0] except Exception: - IP = '127.0.0.1' + IP = "127.0.0.1" finally: s.close() return IP @@ -207,7 +222,9 @@ class Helpers: def get_version(self): version_data = {} try: - with open(os.path.join(self.config_dir, 'version.json'), 'r', encoding='utf-8') as f: + with open( + os.path.join(self.config_dir, "version.json"), "r", encoding="utf-8" + ) as f: version_data = json.load(f) except Exception as e: @@ -217,9 +234,11 @@ class Helpers: @staticmethod def get_announcements(): - r = requests.get('https://craftycontrol.com/notify.json', timeout=2) - data = '[{"id":"1","date":"Unknown","title":"Error getting Announcements","desc":"Error getting ' \ - 'Announcements","link":""}] ' + r = requests.get("https://craftycontrol.com/notify.json", timeout=2) + data = ( + '[{"id":"1","date":"Unknown","title":"Error getting Announcements","desc":"Error getting ' + 'Announcements","link":""}] ' + ) if r.status_code in [200, 201]: try: @@ -229,14 +248,13 @@ class Helpers: return data - def get_version_string(self): version_data = self.get_version() - major = version_data.get('major', '?') - minor = version_data.get('minor', '?') - sub = version_data.get('sub', '?') - meta = version_data.get('meta', '?') + major = version_data.get("major", "?") + minor = version_data.get("minor", "?") + sub = version_data.get("sub", "?") + meta = version_data.get("meta", "?") # set some defaults if we don't get version_data from our helper version = f"{major}.{minor}.{sub}-{meta}" @@ -256,25 +274,31 @@ class Helpers: # our regex replacements # note these are in a tuple - user_keywords = self.get_setting('keywords') + user_keywords = self.get_setting("keywords") replacements = [ - (r'(\[.+?/INFO\])', r'\1'), - (r'(\[.+?/WARN\])', r'\1'), - (r'(\[.+?/ERROR\])', r'\1'), - (r'(\[.+?/FATAL\])', r'\1'), - (r'(\w+?\[/\d+?\.\d+?\.\d+?\.\d+?\:\d+?\])', r'\1'), - (r'\[(\d\d:\d\d:\d\d)\]', r'[\1]'), - (r'(\[.+? INFO\])', r'\1'), - (r'(\[.+? WARN\])', r'\1'), - (r'(\[.+? ERROR\])', r'\1'), - (r'(\[.+? FATAL\])', r'\1') + (r"(\[.+?/INFO\])", r'\1'), + (r"(\[.+?/WARN\])", r'\1'), + (r"(\[.+?/ERROR\])", r'\1'), + (r"(\[.+?/FATAL\])", r'\1'), + ( + r"(\w+?\[/\d+?\.\d+?\.\d+?\.\d+?\:\d+?\])", + r'\1', + ), + (r"\[(\d\d:\d\d:\d\d)\]", r'[\1]'), + (r"(\[.+? INFO\])", r'\1'), + (r"(\[.+? WARN\])", r'\1'), + (r"(\[.+? ERROR\])", r'\1'), + (r"(\[.+? FATAL\])", r'\1'), ] # highlight users keywords for keyword in user_keywords: # pylint: disable=consider-using-f-string - search_replace = (r'({})'.format(keyword), r'\1') + search_replace = ( + r"({})".format(keyword), + r'\1', + ) replacements.append(search_replace) for old, new in replacements: @@ -282,9 +306,8 @@ class Helpers: return line - def validate_traversal(self, base_path, filename): - logger.debug(f"Validating traversal (\"{base_path}\", \"{filename}\")") + logger.debug(f'Validating traversal ("{base_path}", "{filename}")') base = pathlib.Path(base_path).resolve() file = pathlib.Path(filename) fileabs = base.joinpath(file).resolve() @@ -294,7 +317,6 @@ class Helpers: else: raise ValueError("Path traversal detected") - def tail_file(self, file_name, number_lines=20): if not self.check_file_exists(file_name): logger.warning(f"Unable to find file to tail: {file_name}") @@ -307,7 +329,7 @@ class Helpers: line_buffer = number_lines * avg_line_length # open our file - with open(file_name, 'r', encoding='utf-8') as f: + with open(file_name, "r", encoding="utf-8") as f: # seek f.seek(0, 2) @@ -316,14 +338,16 @@ class Helpers: fsize = f.tell() # set pos @ last n chars (buffer from above = number of lines * avg_line_length) - f.seek(max(fsize-line_buffer, 0), 0) + f.seek(max(fsize - line_buffer, 0), 0) # read file til the end try: lines = f.readlines() except Exception as e: - logger.warning(f'Unable to read a line in the file:{file_name} - due to error: {e}') + logger.warning( + f"Unable to read a line in the file:{file_name} - due to error: {e}" + ) # now we are done getting the lines, let's return it return lines @@ -332,7 +356,7 @@ class Helpers: def check_writeable(path: str): filename = os.path.join(path, "tempfile.txt") try: - open(filename, "w", encoding='utf-8').close() + open(filename, "w", encoding="utf-8").close() os.remove(filename) logger.info(f"{filename} is writable") @@ -355,31 +379,36 @@ class Helpers: return False def unzipFile(self, zip_path): - new_dir_list = zip_path.split('/') - new_dir = '' - for i in range(len(new_dir_list)-1): + new_dir_list = zip_path.split("/") + new_dir = "" + for i in range(len(new_dir_list) - 1): if i == 0: new_dir += new_dir_list[i] else: - new_dir += '/'+new_dir_list[i] + new_dir += "/" + new_dir_list[i] if helper.check_file_perms(zip_path) and os.path.isfile(zip_path): helper.ensure_dir_exists(new_dir) tempDir = tempfile.mkdtemp() try: - with zipfile.ZipFile(zip_path, 'r') as zip_ref: + with zipfile.ZipFile(zip_path, "r") as zip_ref: zip_ref.extractall(tempDir) for i in enumerate(zip_ref.filelist): - if len(zip_ref.filelist) > 1 or not zip_ref.filelist[i].filename.endswith('/'): + if len(zip_ref.filelist) > 1 or not zip_ref.filelist[ + i + ].filename.endswith("/"): break full_root_path = tempDir for item in os.listdir(full_root_path): try: - file_helper.move_dir(os.path.join(full_root_path, item), os.path.join(new_dir, item)) + file_helper.move_dir( + os.path.join(full_root_path, item), + os.path.join(new_dir, item), + ) except Exception as ex: - logger.error(f'ERROR IN ZIP IMPORT: {ex}') + logger.error(f"ERROR IN ZIP IMPORT: {ex}") except Exception as ex: print(ex) else: @@ -387,8 +416,8 @@ class Helpers: return def ensure_logging_setup(self): - log_file = os.path.join(os.path.curdir, 'logs', 'commander.log') - session_log_file = os.path.join(os.path.curdir, 'logs', 'session.log') + log_file = os.path.join(os.path.curdir, "logs", "commander.log") + session_log_file = os.path.join(os.path.curdir, "logs", "session.log") logger.info("Checking app directory writable") @@ -402,13 +431,13 @@ class Helpers: # ensure the log directory is there try: with suppress(FileExistsError): - os.makedirs(os.path.join(self.root_dir, 'logs')) + os.makedirs(os.path.join(self.root_dir, "logs")) except Exception as e: console.error(f"Failed to make logs directory with error: {e} ") # ensure the log file is there try: - open(log_file, 'a', encoding='utf-8').close() + open(log_file, "a", encoding="utf-8").close() except Exception as e: console.critical(f"Unable to open log file! {e}") sys.exit(1) @@ -426,7 +455,7 @@ class Helpers: @staticmethod def calc_percent(source_path, dest_path): - #calculates percentable of zip from drive. Not with compression. For backups and support logs + # calculates percentable of zip from drive. Not with compression. For backups and support logs source_size = 0 files_count = 0 for path, _dirs, files in os.walk(source_path): @@ -435,47 +464,41 @@ class Helpers: source_size += os.stat(fp).st_size files_count += 1 dest_size = os.path.getsize(str(dest_path)) - percent = round((dest_size/source_size) * 100, 1) + percent = round((dest_size / source_size) * 100, 1) if percent >= 0: - results = { - "percent": percent, - "total_files": files_count - } + results = {"percent": percent, "total_files": files_count} else: - results = { - "percent": 0, - "total_files": 0 - } + results = {"percent": 0, "total_files": 0} return results @staticmethod def check_file_exists(path: str): - logger.debug(f'Looking for path: {path}') + logger.debug(f"Looking for path: {path}") if os.path.exists(path) and os.path.isfile(path): - logger.debug(f'Found path: {path}') + logger.debug(f"Found path: {path}") return True else: return False @staticmethod - def human_readable_file_size(num: int, suffix='B'): - for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']: + def human_readable_file_size(num: int, suffix="B"): + for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]: if abs(num) < 1024.0: - # pylint: disable=consider-using-f-string + # pylint: disable=consider-using-f-string return "%3.1f%s%s" % (num, unit, suffix) num /= 1024.0 # pylint: disable=consider-using-f-string - return "%.1f%s%s" % (num, 'Y', suffix) + return "%.1f%s%s" % (num, "Y", suffix) @staticmethod def check_path_exists(path: str): if not path: return False - logger.debug(f'Looking for path: {path}') + logger.debug(f"Looking for path: {path}") if os.path.exists(path): - logger.debug(f'Found path: {path}') + logger.debug(f"Found path: {path}") return True else: return False @@ -483,12 +506,12 @@ class Helpers: @staticmethod def get_file_contents(path: str, lines=100): - contents = '' + contents = "" if os.path.exists(path) and os.path.isfile(path): try: - with open(path, 'r', encoding='utf-8') as f: - for line in (f.readlines() [-lines:]): + with open(path, "r", encoding="utf-8") as f: + for line in f.readlines()[-lines:]: contents = contents + line return contents @@ -497,7 +520,9 @@ class Helpers: logger.error(f"Unable to read file: {path}. \n Error: {e}") return False else: - logger.error(f"Unable to read file: {path}. File not found, or isn't a file.") + logger.error( + f"Unable to read file: {path}. File not found, or isn't a file." + ) return False def create_session_file(self, ignore=False): @@ -510,30 +535,33 @@ class Helpers: file_data = self.get_file_contents(self.session_file) try: data = json.loads(file_data) - pid = data.get('pid') - started = data.get('started') + pid = data.get("pid") + started = data.get("started") if psutil.pid_exists(pid): - console.critical(f"Another Crafty Controller agent seems to be running...\npid: {pid} \nstarted on: {started}") + console.critical( + f"Another Crafty Controller agent seems to be running...\npid: {pid} \nstarted on: {started}" + ) logger.critical("Found running crafty process. Exiting.") sys.exit(1) else: - logger.info("No process found for pid. Assuming crafty crashed. Deleting stale session.lock") + logger.info( + "No process found for pid. Assuming crafty crashed. Deleting stale session.lock" + ) os.remove(self.session_file) except Exception as e: logger.error(f"Failed to locate existing session.lock with error: {e} ") - console.error(f"Failed to locate existing session.lock with error: {e} ") + console.error( + f"Failed to locate existing session.lock with error: {e} " + ) sys.exit(1) pid = os.getpid() now = datetime.now() - session_data = { - 'pid': pid, - 'started': now.strftime("%d-%m-%Y, %H:%M:%S") - } - with open(self.session_file, 'w', encoding='utf-8') as f: + session_data = {"pid": pid, "started": now.strftime("%d-%m-%Y, %H:%M:%S")} + with open(self.session_file, "w", encoding="utf-8") as f: json.dump(session_data, f, indent=True) # because this is a recursive function, we will return bytes, and set human readable later @@ -548,26 +576,30 @@ class Helpers: @staticmethod def list_dir_by_date(path: str, reverse=False): - return [str(p) for p in sorted(pathlib.Path(path).iterdir(), key=os.path.getmtime, reverse=reverse)] + return [ + str(p) + for p in sorted( + pathlib.Path(path).iterdir(), key=os.path.getmtime, reverse=reverse + ) + ] def get_human_readable_files_sizes(self, paths: list): sizes = [] for p in paths: - sizes.append({ - "path": p, - "size": self.human_readable_file_size(os.stat(p).st_size) - }) + sizes.append( + {"path": p, "size": self.human_readable_file_size(os.stat(p).st_size)} + ) return sizes @staticmethod def base64_encode_string(fun_str: str): - s_bytes = str(fun_str).encode('utf-8') + s_bytes = str(fun_str).encode("utf-8") b64_bytes = base64.encodebytes(s_bytes) - return b64_bytes.decode('utf-8') + return b64_bytes.decode("utf-8") @staticmethod def base64_decode_string(fun_str: str): - s_bytes = str(fun_str).encode('utf-8') + s_bytes = str(fun_str).encode("utf-8") b64_bytes = base64.decodebytes(s_bytes) return b64_bytes.decode("utf-8") @@ -666,23 +698,23 @@ class Helpers: random_generator() = G8sjO2 random_generator(3, abcdef) = adf """ - return ''.join(random.choice(chars) for x in range(size)) + return "".join(random.choice(chars) for x in range(size)) @staticmethod def is_os_windows(): - if os.name == 'nt': + if os.name == "nt": return True else: return False @staticmethod def wtol_path(w_path): - l_path = w_path.replace('\\', '/') + l_path = w_path.replace("\\", "/") return l_path @staticmethod def ltow_path(l_path): - w_path = l_path.replace('/', '\\') + w_path = l_path.replace("/", "\\") return w_path @staticmethod @@ -694,10 +726,10 @@ class Helpers: data = {} if self.check_file_exists(default_file): - with open(default_file, 'r', encoding='utf-8') as f: + with open(default_file, "r", encoding="utf-8") as f: data = json.load(f) - del_json = helper.get_setting('delete_default_json') + del_json = helper.get_setting("delete_default_json") if del_json: os.remove(default_file) @@ -714,14 +746,15 @@ class Helpers: dir_list.append(item) else: unsorted_files.append(item) - file_list = sorted(dir_list, key=str.casefold) + sorted(unsorted_files, key=str.casefold) + file_list = sorted(dir_list, key=str.casefold) + sorted( + unsorted_files, key=str.casefold + ) for raw_filename in file_list: filename = html.escape(raw_filename) rel = os.path.join(folder, raw_filename) dpath = os.path.join(folder, filename) if os.path.isdir(rel): - output += \ - f"""
  • + output += f"""
  • \n
    @@ -729,8 +762,7 @@ class Helpers: {filename}
  • - \n"""\ - + \n""" else: if filename != "crafty_managed.txt": output += f"""
  • """\ - + file_list = sorted(dir_list, key=str.casefold) + sorted( + unsorted_files, key=str.casefold + ) + output += f"""
      """ for raw_filename in file_list: filename = html.escape(raw_filename) dpath = os.path.join(folder, filename) rel = os.path.join(folder, raw_filename) if os.path.isdir(rel): - output += \ - f"""
    • + output += f"""
    • \n
      {filename} -
    • """\ - +
    • """ else: if filename != "crafty_managed.txt": output += f"""
    • {filename}
    • """ - output += '
    \n' + output += "\n" return output @staticmethod def generate_zip_tree(folder, output=""): file_list = os.listdir(folder) file_list = sorted(file_list, key=str.casefold) - output += \ - f"""
      """\ - + output += f"""
        """ for raw_filename in file_list: filename = html.escape(raw_filename) rel = os.path.join(folder, raw_filename) dpath = os.path.join(folder, filename) if os.path.isdir(rel): - output += \ - f"""
      • + output += f"""
      • \n
        @@ -801,24 +828,20 @@ class Helpers: {filename}
      • - \n"""\ - + \n""" return output @staticmethod def generate_zip_dir(folder, output=""): file_list = os.listdir(folder) file_list = sorted(file_list, key=str.casefold) - output += \ - f"""
          """\ - + output += f"""
            """ for raw_filename in file_list: filename = html.escape(raw_filename) rel = os.path.join(folder, raw_filename) dpath = os.path.join(folder, filename) if os.path.isdir(rel): - output += \ - f"""
          • + output += f"""
          • \n
            @@ -826,35 +849,33 @@ class Helpers: {filename} -
          • """\ - +
          • """ return output @staticmethod def unzipServer(zip_path, user_id): if helper.check_file_perms(zip_path): tempDir = tempfile.mkdtemp() - with zipfile.ZipFile(zip_path, 'r') as zip_ref: - #extracts archive to temp directory + with zipfile.ZipFile(zip_path, "r") as zip_ref: + # extracts archive to temp directory zip_ref.extractall(tempDir) if user_id: - websocket_helper.broadcast_user(user_id, 'send_temp_path',{ - 'path': tempDir - }) + websocket_helper.broadcast_user( + user_id, "send_temp_path", {"path": tempDir} + ) + @staticmethod def backup_select(path, user_id): if user_id: - websocket_helper.broadcast_user(user_id, 'send_temp_path',{ - 'path': path - }) + websocket_helper.broadcast_user(user_id, "send_temp_path", {"path": path}) @staticmethod def unzip_backup_archive(backup_path, zip_name): zip_path = os.path.join(backup_path, zip_name) if helper.check_file_perms(zip_path): tempDir = tempfile.mkdtemp() - with zipfile.ZipFile(zip_path, 'r') as zip_ref: - #extracts archive to temp directory + with zipfile.ZipFile(zip_path, "r") as zip_ref: + # extracts archive to temp directory zip_ref.extractall(tempDir) return tempDir else: @@ -869,7 +890,9 @@ class Helpers: # Compare the common path of the parent and child path with the common path of just the parent path. # Using the commonpath method on just the parent path will regularise the path name in the same way # as the comparison that deals with both paths, removing any trailing path separator - return os.path.commonpath([parent_path]) == os.path.commonpath([parent_path, child_path]) + return os.path.commonpath([parent_path]) == os.path.commonpath( + [parent_path, child_path] + ) @staticmethod def in_path_old(x, y): @@ -901,20 +924,20 @@ class Helpers: return False return True - @staticmethod def remove_prefix(text, prefix): if text.startswith(prefix): - return text[len(prefix):] + return text[len(prefix) :] return text @staticmethod def getLangPage(text): lang = text.split("_")[0] region = text.split("_")[1] - if region == 'EN': - return 'en' + if region == "EN": + return "en" else: - return lang+"-"+region + return lang + "-" + region + helper = Helpers() diff --git a/app/classes/shared/import3.py b/app/classes/shared/import3.py index dfcd5bcf..024dd444 100644 --- a/app/classes/shared/import3.py +++ b/app/classes/shared/import3.py @@ -8,20 +8,26 @@ from app.classes.shared.console import console logger = logging.getLogger(__name__) -class import3: +class import3: def __init__(self): self.controller = Controller() def start_import(self): - folder = os.path.normpath(input("Please input the path to the migrations folder in your installation of Crafty 3: ")) + folder = os.path.normpath( + input( + "Please input the path to the migrations folder in your installation of Crafty 3: " + ) + ) if not os.path.exists(folder): - console.info("Crafty cannot find the path you entered. Does Crafty's user have permission to access it?") + console.info( + "Crafty cannot find the path you entered. Does Crafty's user have permission to access it?" + ) console.info("Please run the import3 command again and enter a valid path.") else: - with open (os.path.join(folder, "users.json"), encoding="utf-8") as f: + with open(os.path.join(folder, "users.json"), encoding="utf-8") as f: user_json = json.loads(f.read()) - with open (os.path.join(folder, "mc_settings.json"), encoding="utf-8") as f: + with open(os.path.join(folder, "mc_settings.json"), encoding="utf-8") as f: servers_json = json.loads(f.read()) self.import_users(user_json) self.import_servers(servers_json, self.controller) @@ -31,28 +37,51 @@ class import3: # If there is only one user to import json needs to call the data differently if isinstance(json_data, list): for user in json_data: - users_helper.add_rawpass_user(user['username'], user['password']) + users_helper.add_rawpass_user(user["username"], user["password"]) console.info(f"Imported user {user['username']} from Crafty 3") logger.info(f"Imported user {user['username']} from Crafty 3") else: - console.info("There is only one user detected. Cannot create duplicate Admin account.") - logger.info("There is only one user detected. Cannot create duplicate Admin account.") + console.info( + "There is only one user detected. Cannot create duplicate Admin account." + ) + logger.info( + "There is only one user detected. Cannot create duplicate Admin account." + ) @staticmethod def import_servers(json_data, controller): # If there is only one server to import json needs to call the data differently if isinstance(json_data, list): for server in json_data: - new_server_id = controller.import_jar_server(server_name=server['server_name'], server_path=server['server_path'], - server_jar=server['server_jar'], min_mem=(int(server['memory_min'])/1000), - max_mem=(int(server['memory_max'])/1000), port=server['server_port']) - console.info(f"Imported server {server['server_name']}[{server['id']}] from Crafty 3 to new server id {new_server_id}") - logger.info(f"Imported server {server['server_name']}[{server['id']}] from Crafty 3 to new server id {new_server_id}") + new_server_id = controller.import_jar_server( + server_name=server["server_name"], + server_path=server["server_path"], + server_jar=server["server_jar"], + min_mem=(int(server["memory_min"]) / 1000), + max_mem=(int(server["memory_max"]) / 1000), + port=server["server_port"], + ) + console.info( + f"Imported server {server['server_name']}[{server['id']}] from Crafty 3 to new server id {new_server_id}" + ) + logger.info( + f"Imported server {server['server_name']}[{server['id']}] from Crafty 3 to new server id {new_server_id}" + ) else: - new_server_id = controller.import_jar_server(server_name=json_data['server_name'], server_path=json_data['server_path'], - server_jar=json_data['server_jar'], min_mem=(int(json_data['memory_min'])/1000), - max_mem=(int(json_data['memory_max'])/1000), port=json_data['server_port']) - console.info(f"Imported server {json_data['server_name']}[{json_data['id']}] from Crafty 3 to new server id {new_server_id}") - logger.info(f"Imported server {json_data['server_name']}[{json_data['id']}] from Crafty 3 to new server id {new_server_id}") + new_server_id = controller.import_jar_server( + server_name=json_data["server_name"], + server_path=json_data["server_path"], + server_jar=json_data["server_jar"], + min_mem=(int(json_data["memory_min"]) / 1000), + max_mem=(int(json_data["memory_max"]) / 1000), + port=json_data["server_port"], + ) + console.info( + f"Imported server {json_data['server_name']}[{json_data['id']}] from Crafty 3 to new server id {new_server_id}" + ) + logger.info( + f"Imported server {json_data['server_name']}[{json_data['id']}] from Crafty 3 to new server id {new_server_id}" + ) + import3 = import3() diff --git a/app/classes/shared/installer.py b/app/classes/shared/installer.py index 3ec90d8a..801c4ae0 100644 --- a/app/classes/shared/installer.py +++ b/app/classes/shared/installer.py @@ -1,12 +1,13 @@ import sys import subprocess -class install: +class install: @staticmethod def is_venv(): - return (hasattr(sys, 'real_prefix') or - (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix)) + return hasattr(sys, "real_prefix") or ( + hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix + ) def do_install(self): @@ -16,8 +17,11 @@ class install: sys.exit(1) # do our pip install - subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", 'requirements.txt']) + subprocess.check_call( + [sys.executable, "-m", "pip", "install", "-r", "requirements.txt"] + ) print("Crafty has installed it's dependencies, please restart Crafty") sys.exit(0) + installer = install() diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index 65a74e19..eefefb7e 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -28,7 +28,8 @@ from app.classes.web.websocket_helper import websocket_helper try: from peewee import DoesNotExist - #TZLocal is set as a hidden import on win pipeline + + # TZLocal is set as a hidden import on win pipeline from tzlocal import get_localzone from apscheduler.schedulers.background import BackgroundScheduler @@ -37,8 +38,8 @@ except ModuleNotFoundError as err: logger = logging.getLogger(__name__) -class Controller: +class Controller: def __init__(self): self.servers_list = [] self.stats = Stats(self) @@ -57,12 +58,14 @@ class Controller: logger.info(f"Checking to see if we already registered {server_id_to_check}") for s in self.servers_list: - known_server = s.get('server_id') + known_server = s.get("server_id") if known_server is None: return False if known_server == server_id_to_check: - logger.info(f'skipping initialization of server {server_id_to_check} because it is already loaded') + logger.info( + f"skipping initialization of server {server_id_to_check} because it is already loaded" + ) return True return False @@ -72,20 +75,28 @@ class Controller: servers = self.servers.get_all_defined_servers() for s in servers: - server_id = s.get('server_id') + server_id = s.get("server_id") # if we have already initialized this server, let's skip it. if self.check_server_loaded(server_id): continue # if this server path no longer exists - let's warn and bomb out - if not helper.check_path_exists(helper.get_os_understandable_path(s['path'])): - logger.warning(f"Unable to find server {s['server_name']} at path {s['path']}. Skipping this server") + if not helper.check_path_exists( + helper.get_os_understandable_path(s["path"]) + ): + logger.warning( + f"Unable to find server {s['server_name']} at path {s['path']}. Skipping this server" + ) - console.warning(f"Unable to find server {s['server_name']} at path {s['path']}. Skipping this server") + console.warning( + f"Unable to find server {s['server_name']} at path {s['path']}. Skipping this server" + ) continue - settings_file = os.path.join(helper.get_os_understandable_path(s['path']), 'server.properties') + settings_file = os.path.join( + helper.get_os_understandable_path(s["path"]), "server.properties" + ) # if the properties file isn't there, let's warn if not helper.check_file_exists(settings_file): @@ -96,27 +107,29 @@ class Controller: settings = ServerProps(settings_file) temp_server_dict = { - 'server_id': s.get('server_id'), - 'server_data_obj': s, - 'server_obj': Server(self.stats), - 'server_settings': settings.props + "server_id": s.get("server_id"), + "server_data_obj": s, + "server_obj": Server(self.stats), + "server_settings": settings.props, } # setup the server, do the auto start and all that jazz - temp_server_dict['server_obj'].do_server_setup(s) + temp_server_dict["server_obj"].do_server_setup(s) # add this temp object to the list of init servers self.servers_list.append(temp_server_dict) - if s['auto_start']: - self.servers.set_waiting_start(s['server_id'], True) + if s["auto_start"]: + self.servers.set_waiting_start(s["server_id"], True) - self.refresh_server_settings(s['server_id']) + self.refresh_server_settings(s["server_id"]) - console.info(f"Loaded Server: ID {s['server_id']}" + - f" | Name: {s['server_name']}" + - f" | Autostart: {s['auto_start']}" + - f" | Delay: {s['auto_start_delay']} ") + console.info( + f"Loaded Server: ID {s['server_id']}" + + f" | Name: {s['server_name']}" + + f" | Autostart: {s['auto_start']}" + + f" | Delay: {s['auto_start_delay']} " + ) def refresh_server_settings(self, server_id: int): server_obj = self.get_server_obj(server_id) @@ -132,80 +145,104 @@ class Controller: def set_project_root(self, root_dir): self.project_root = root_dir - def package_support_logs(self, exec_user): - if exec_user['preparing']: + if exec_user["preparing"]: return - self.users.set_prepare(exec_user['user_id']) - #pausing so on screen notifications can run for user + self.users.set_prepare(exec_user["user_id"]) + # pausing so on screen notifications can run for user time.sleep(7) - websocket_helper.broadcast_user(exec_user['user_id'], 'notification', 'Preparing your support logs') + websocket_helper.broadcast_user( + exec_user["user_id"], "notification", "Preparing your support logs" + ) tempDir = tempfile.mkdtemp() tempZipStorage = tempfile.mkdtemp() - full_temp = os.path.join(tempDir, 'support_logs') + full_temp = os.path.join(tempDir, "support_logs") os.mkdir(full_temp) tempZipStorage = os.path.join(tempZipStorage, "support_logs") crafty_path = os.path.join(full_temp, "crafty") os.mkdir(crafty_path) server_path = os.path.join(full_temp, "server") os.mkdir(server_path) - if exec_user['superuser']: + if exec_user["superuser"]: auth_servers = self.servers.get_all_defined_servers() else: - user_servers = self.servers.get_authorized_servers(int(exec_user['user_id'])) + user_servers = self.servers.get_authorized_servers( + int(exec_user["user_id"]) + ) auth_servers = [] for server in user_servers: - if Enum_Permissions_Server.Logs in self.server_perms.get_user_id_permissions_list(exec_user['user_id'], server["server_id"]): + if ( + Enum_Permissions_Server.Logs + in self.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server["server_id"] + ) + ): auth_servers.append(server) else: - logger.info(f"Logs permission not available for server {server['server_name']}. Skipping.") - #we'll iterate through our list of log paths from auth servers. + logger.info( + f"Logs permission not available for server {server['server_name']}. Skipping." + ) + # we'll iterate through our list of log paths from auth servers. for server in auth_servers: - final_path = os.path.join(server_path, str(server['server_name'])) + final_path = os.path.join(server_path, str(server["server_name"])) try: os.mkdir(final_path) except FileExistsError: - final_path += '_'+server['server_uuid'] + final_path += "_" + server["server_uuid"] os.mkdir(final_path) try: - file_helper.copy_file(server['log_path'], final_path) + file_helper.copy_file(server["log_path"], final_path) except Exception as e: logger.warning(f"Failed to copy file with error: {e}") - #Copy crafty logs to archive dir - full_log_name = os.path.join(crafty_path, 'logs') - file_helper.copy_dir(os.path.join(self.project_root, 'logs'), full_log_name) - self.support_scheduler.add_job(self.log_status, 'interval', seconds=1, id="logs_"+str(exec_user['user_id']), args = [full_temp, - tempZipStorage +'.zip', exec_user]) + # Copy crafty logs to archive dir + full_log_name = os.path.join(crafty_path, "logs") + file_helper.copy_dir(os.path.join(self.project_root, "logs"), full_log_name) + self.support_scheduler.add_job( + self.log_status, + "interval", + seconds=1, + id="logs_" + str(exec_user["user_id"]), + args=[full_temp, tempZipStorage + ".zip", exec_user], + ) file_helper.make_archive(tempZipStorage, tempDir) if len(websocket_helper.clients) > 0: - websocket_helper.broadcast_user(exec_user['user_id'], 'support_status_update', helper.calc_percent(full_temp, tempZipStorage +'.zip')) + websocket_helper.broadcast_user( + exec_user["user_id"], + "support_status_update", + helper.calc_percent(full_temp, tempZipStorage + ".zip"), + ) - tempZipStorage += '.zip' - websocket_helper.broadcast_user(exec_user['user_id'], 'send_logs_bootbox', { - }) + tempZipStorage += ".zip" + websocket_helper.broadcast_user(exec_user["user_id"], "send_logs_bootbox", {}) - self.users.set_support_path(exec_user['user_id'], tempZipStorage) + self.users.set_support_path(exec_user["user_id"], tempZipStorage) - self.users.stop_prepare(exec_user['user_id']) - self.support_scheduler.remove_job('logs_'+str(exec_user["user_id"])) + self.users.stop_prepare(exec_user["user_id"]) + self.support_scheduler.remove_job("logs_" + str(exec_user["user_id"])) @staticmethod def add_system_user(): - helper_users.add_user("system", helper.random_string_generator(64), "default@example.com", False, False) + helper_users.add_user( + "system", + helper.random_string_generator(64), + "default@example.com", + False, + False, + ) def get_server_settings(self, server_id): for s in self.servers_list: - if int(s['server_id']) == int(server_id): - return s['server_settings'] + if int(s["server_id"]) == int(server_id): + return s["server_settings"] logger.warning(f"Unable to find server object for server id {server_id}") return False def crash_detection(self, server_obj): svr = self.get_server_obj(server_obj.server_id) - #start or stop crash detection depending upon user preference - #The below functions check to see if the server is running. They only execute if it's running. + # start or stop crash detection depending upon user preference + # The below functions check to see if the server is running. They only execute if it's running. if server_obj.crash_detection == 1: svr.start_crash_detection() else: @@ -216,29 +253,28 @@ class Controller: self.log_stats = results if len(websocket_helper.clients) > 0: - websocket_helper.broadcast_user(exec_user['user_id'], 'support_status_update', results) + websocket_helper.broadcast_user( + exec_user["user_id"], "support_status_update", results + ) def send_log_status(self): try: return self.log_stats except: - return { - 'percent': 0, - 'total_files': 0 - } + return {"percent": 0, "total_files": 0} def get_server_obj(self, server_id: Union[str, int]) -> Union[bool, Server]: for s in self.servers_list: - if str(s['server_id']) == str(server_id): - return s['server_obj'] + if str(s["server_id"]) == str(server_id): + return s["server_obj"] logger.warning(f"Unable to find server object for server id {server_id}") return False # TODO: Change to None def get_server_data(self, server_id: str): for s in self.servers_list: - if str(s['server_id']) == str(server_id): - return s['server_data_obj'] + if str(s["server_id"]) == str(server_id): + return s["server_data_obj"] logger.warning(f"Unable to find server object for server id {server_id}") return False @@ -255,14 +291,11 @@ class Controller: for s in self.servers_list: # is the server running? - srv_obj = s['server_obj'] + srv_obj = s["server_obj"] running = srv_obj.check_running() # if so, let's add a dictionary to the list of running servers if running: - running_servers.append({ - 'id': srv_obj.server_id, - 'name': srv_obj.name - }) + running_servers.append({"id": srv_obj.server_id, "name": srv_obj.name}) return running_servers @@ -278,7 +311,7 @@ class Controller: logger.info(f"Stopping Server ID {s['id']} - {s['name']}") console.info(f"Stopping Server ID {s['id']} - {s['name']}") - self.stop_server(s['id']) + self.stop_server(s["id"]) # let's wait 2 seconds to let everything flush out time.sleep(2) @@ -291,15 +324,23 @@ class Controller: svr_obj = self.get_server_obj(server_id) svr_obj.stop_threaded_server() - def create_jar_server(self, server: str, version: str, name: str, min_mem: int, max_mem: int, port: int): + def create_jar_server( + self, + server: str, + version: str, + name: str, + min_mem: int, + max_mem: int, + port: int, + ): server_id = helper.create_uuid() server_dir = os.path.join(helper.servers_dir, server_id) backup_path = os.path.join(helper.backup_path, server_id) if helper.is_os_windows(): server_dir = helper.wtol_path(server_dir) backup_path = helper.wtol_path(backup_path) - server_dir.replace(' ', '^ ') - backup_path.replace(' ', '^ ') + server_dir.replace(" ", "^ ") + backup_path.replace(" ", "^ ") server_file = f"{server}-{version}.jar" full_jar_path = os.path.join(server_dir, server_file) @@ -310,12 +351,14 @@ class Controller: try: # do a eula.txt - with open(os.path.join(server_dir, "eula.txt"), 'w', encoding='utf-8') as f: + with open(os.path.join(server_dir, "eula.txt"), "w", encoding="utf-8") as f: f.write("eula=false") f.close() # setup server.properties with the port - with open(os.path.join(server_dir, "server.properties"), "w", encoding='utf-8') as f: + with open( + os.path.join(server_dir, "server.properties"), "w", encoding="utf-8" + ) as f: f.write(f"server-port={port}") f.close() @@ -323,16 +366,26 @@ class Controller: logger.error(f"Unable to create required server files due to :{e}") return False - #must remain non-fstring due to string addtion + # must remain non-fstring due to string addtion if helper.is_os_windows(): server_command = f'java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar "{full_jar_path}" nogui' else: - server_command = f'java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar {full_jar_path} nogui' + server_command = f"java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar {full_jar_path} nogui" server_log_file = f"{server_dir}/logs/latest.log" server_stop = "stop" - new_id = self.register_server(name, server_id, server_dir, backup_path, server_command, server_file, server_log_file, server_stop, - port, server_type='minecraft-java') + new_id = self.register_server( + name, + server_id, + server_dir, + backup_path, + server_command, + server_file, + server_log_file, + server_stop, + port, + server_type="minecraft-java", + ) # download the jar server_jar_obj.download_jar(server, version, full_jar_path, new_id) @@ -340,7 +393,7 @@ class Controller: return new_id @staticmethod - def verify_jar_server( server_path: str, server_jar: str): + def verify_jar_server(server_path: str, server_jar: str): server_path = helper.get_os_understandable_path(server_path) path_check = helper.check_path_exists(server_path) jar_check = helper.check_file_exists(os.path.join(server_path, server_jar)) @@ -356,15 +409,23 @@ class Controller: return False return True - def import_jar_server(self, server_name: str, server_path: str, server_jar: str, min_mem: int, max_mem: int, port: int): + def import_jar_server( + self, + server_name: str, + server_path: str, + server_jar: str, + min_mem: int, + max_mem: int, + port: int, + ): server_id = helper.create_uuid() new_server_dir = os.path.join(helper.servers_dir, server_id) backup_path = os.path.join(helper.backup_path, server_id) if helper.is_os_windows(): new_server_dir = helper.wtol_path(new_server_dir) backup_path = helper.wtol_path(backup_path) - new_server_dir.replace(' ', '^ ') - backup_path.replace(' ', '^ ') + new_server_dir.replace(" ", "^ ") + backup_path.replace(" ", "^ ") helper.ensure_dir_exists(new_server_dir) helper.ensure_dir_exists(backup_path) @@ -376,87 +437,129 @@ class Controller: has_properties = False for item in os.listdir(new_server_dir): - if str(item) == 'server.properties': + if str(item) == "server.properties": has_properties = True if not has_properties: - logger.info(f"No server.properties found on zip file import. Creating one with port selection of {str(port)}") - with open(os.path.join(new_server_dir, "server.properties"), "w", encoding='utf-8') as f: + logger.info( + f"No server.properties found on zip file import. Creating one with port selection of {str(port)}" + ) + with open( + os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" + ) as f: f.write(f"server-port={port}") f.close() full_jar_path = os.path.join(new_server_dir, server_jar) - #due to adding strings this must not be an fstring + # due to adding strings this must not be an fstring if helper.is_os_windows(): server_command = f'java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar "{full_jar_path}" nogui' else: - server_command = f'java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar {full_jar_path} nogui' + server_command = f"java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar {full_jar_path} nogui" server_log_file = f"{new_server_dir}/logs/latest.log" server_stop = "stop" - new_id = self.register_server(server_name, server_id, new_server_dir, backup_path, server_command, server_jar, - server_log_file, server_stop, port, server_type='minecraft-java') + new_id = self.register_server( + server_name, + server_id, + new_server_dir, + backup_path, + server_command, + server_jar, + server_log_file, + server_stop, + port, + server_type="minecraft-java", + ) return new_id - def import_zip_server(self, server_name: str, zip_path: str, server_jar: str, min_mem: int, max_mem: int, port: int): + def import_zip_server( + self, + server_name: str, + zip_path: str, + server_jar: str, + min_mem: int, + max_mem: int, + port: int, + ): server_id = helper.create_uuid() new_server_dir = os.path.join(helper.servers_dir, server_id) backup_path = os.path.join(helper.backup_path, server_id) if helper.is_os_windows(): new_server_dir = helper.wtol_path(new_server_dir) backup_path = helper.wtol_path(backup_path) - new_server_dir.replace(' ', '^ ') - backup_path.replace(' ', '^ ') + new_server_dir.replace(" ", "^ ") + backup_path.replace(" ", "^ ") tempDir = helper.get_os_understandable_path(zip_path) helper.ensure_dir_exists(new_server_dir) helper.ensure_dir_exists(backup_path) has_properties = False - #extracts archive to temp directory + # extracts archive to temp directory for item in os.listdir(tempDir): - if str(item) == 'server.properties': + if str(item) == "server.properties": has_properties = True try: if not os.path.isdir(os.path.join(tempDir, item)): - file_helper.move_file(os.path.join(tempDir, item), os.path.join(new_server_dir, item)) + file_helper.move_file( + os.path.join(tempDir, item), os.path.join(new_server_dir, item) + ) else: - file_helper.move_dir(os.path.join(tempDir, item), os.path.join(new_server_dir, item)) + file_helper.move_dir( + os.path.join(tempDir, item), os.path.join(new_server_dir, item) + ) except Exception as ex: - logger.error(f'ERROR IN ZIP IMPORT: {ex}') + logger.error(f"ERROR IN ZIP IMPORT: {ex}") if not has_properties: - logger.info(f"No server.properties found on zip file import. Creating one with port selection of {str(port)}") - with open(os.path.join(new_server_dir, "server.properties"), "w", encoding='utf-8') as f: + logger.info( + f"No server.properties found on zip file import. Creating one with port selection of {str(port)}" + ) + with open( + os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" + ) as f: f.write(f"server-port={port}") f.close() full_jar_path = os.path.join(new_server_dir, server_jar) - #due to strings being added we need to leave this as not an fstring + # due to strings being added we need to leave this as not an fstring if helper.is_os_windows(): server_command = f'java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar "{full_jar_path}" nogui' else: - server_command = f'java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar {full_jar_path} nogui' - logger.debug('command: ' + server_command) + server_command = f"java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar {full_jar_path} nogui" + logger.debug("command: " + server_command) server_log_file = f"{new_server_dir}/logs/latest.log" server_stop = "stop" - new_id = self.register_server(server_name, server_id, new_server_dir, backup_path, server_command, server_jar, - server_log_file, server_stop, port, server_type='minecraft-java') + new_id = self.register_server( + server_name, + server_id, + new_server_dir, + backup_path, + server_command, + server_jar, + server_log_file, + server_stop, + port, + server_type="minecraft-java", + ) return new_id - #************************************************************************************************ + # ************************************************************************************************ # BEDROCK IMPORTS - #************************************************************************************************ + # ************************************************************************************************ - def import_bedrock_server(self, server_name: str, server_path: str, server_exe: str, port: int): + def import_bedrock_server( + self, server_name: str, server_path: str, server_exe: str, port: int + ): server_id = helper.create_uuid() new_server_dir = os.path.join(helper.servers_dir, server_id) backup_path = os.path.join(helper.backup_path, server_id) if helper.is_os_windows(): new_server_dir = helper.wtol_path(new_server_dir) backup_path = helper.wtol_path(backup_path) - new_server_dir.replace(' ', '^ ') - backup_path.replace(' ', '^ ') + new_server_dir.replace(" ", "^ ") + backup_path.replace(" ", "^ ") helper.ensure_dir_exists(new_server_dir) helper.ensure_dir_exists(backup_path) @@ -468,96 +571,132 @@ class Controller: has_properties = False for item in os.listdir(new_server_dir): - if str(item) == 'server.properties': + if str(item) == "server.properties": has_properties = True if not has_properties: - logger.info(f"No server.properties found on zip file import. Creating one with port selection of {str(port)}") - with open(os.path.join(new_server_dir, "server.properties"), "w", encoding='utf-8') as f: + logger.info( + f"No server.properties found on zip file import. Creating one with port selection of {str(port)}" + ) + with open( + os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" + ) as f: f.write(f"server-port={port}") f.close() full_jar_path = os.path.join(new_server_dir, server_exe) - #due to adding strings this must not be an fstring + # due to adding strings this must not be an fstring if helper.is_os_windows(): server_command = f'"{full_jar_path}"' else: - server_command = f'./{server_exe}' - logger.debug('command: ' + server_command) + server_command = f"./{server_exe}" + logger.debug("command: " + server_command) server_log_file = "N/A" server_stop = "stop" - new_id = self.register_server(server_name, server_id, new_server_dir, backup_path, server_command, server_exe, - server_log_file, server_stop, port, server_type='minecraft-bedrock') + new_id = self.register_server( + server_name, + server_id, + new_server_dir, + backup_path, + server_command, + server_exe, + server_log_file, + server_stop, + port, + server_type="minecraft-bedrock", + ) if os.name != "nt": if helper.check_file_exists(full_jar_path): os.chmod(full_jar_path, 0o2775) return new_id - def import_bedrock_zip_server(self, server_name: str, zip_path: str, server_exe: str, port: int): + def import_bedrock_zip_server( + self, server_name: str, zip_path: str, server_exe: str, port: int + ): server_id = helper.create_uuid() new_server_dir = os.path.join(helper.servers_dir, server_id) backup_path = os.path.join(helper.backup_path, server_id) if helper.is_os_windows(): new_server_dir = helper.wtol_path(new_server_dir) backup_path = helper.wtol_path(backup_path) - new_server_dir.replace(' ', '^ ') - backup_path.replace(' ', '^ ') + new_server_dir.replace(" ", "^ ") + backup_path.replace(" ", "^ ") tempDir = helper.get_os_understandable_path(zip_path) helper.ensure_dir_exists(new_server_dir) helper.ensure_dir_exists(backup_path) has_properties = False - #extracts archive to temp directory + # extracts archive to temp directory for item in os.listdir(tempDir): - if str(item) == 'server.properties': + if str(item) == "server.properties": has_properties = True try: if not os.path.isdir(os.path.join(tempDir, item)): - file_helper.move_file(os.path.join(tempDir, item), os.path.join(new_server_dir, item)) + file_helper.move_file( + os.path.join(tempDir, item), os.path.join(new_server_dir, item) + ) else: - file_helper.move_dir(os.path.join(tempDir, item), os.path.join(new_server_dir, item)) + file_helper.move_dir( + os.path.join(tempDir, item), os.path.join(new_server_dir, item) + ) except Exception as ex: - logger.error(f'ERROR IN ZIP IMPORT: {ex}') + logger.error(f"ERROR IN ZIP IMPORT: {ex}") if not has_properties: - logger.info(f"No server.properties found on zip file import. Creating one with port selection of {str(port)}") - with open(os.path.join(new_server_dir, "server.properties"), "w", encoding='utf-8') as f: + logger.info( + f"No server.properties found on zip file import. Creating one with port selection of {str(port)}" + ) + with open( + os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" + ) as f: f.write(f"server-port={port}") f.close() full_jar_path = os.path.join(new_server_dir, server_exe) - #due to strings being added we need to leave this as not an fstring + # due to strings being added we need to leave this as not an fstring if helper.is_os_windows(): server_command = f'"{full_jar_path}"' else: - server_command = f'./{server_exe}' - logger.debug('command: ' + server_command) + server_command = f"./{server_exe}" + logger.debug("command: " + server_command) server_log_file = "N/A" server_stop = "stop" - new_id = self.register_server(server_name, server_id, new_server_dir, backup_path, server_command, server_exe, - server_log_file, server_stop, port, server_type='minecraft-bedrock') + new_id = self.register_server( + server_name, + server_id, + new_server_dir, + backup_path, + server_command, + server_exe, + server_log_file, + server_stop, + port, + server_type="minecraft-bedrock", + ) if os.name != "nt": if helper.check_file_exists(full_jar_path): os.chmod(full_jar_path, 0o2775) return new_id - #************************************************************************************************ + # ************************************************************************************************ # BEDROCK IMPORTS END - #************************************************************************************************ + # ************************************************************************************************ def rename_backup_dir(self, old_server_id, new_server_id, new_uuid): server_data = self.servers.get_server_data_by_id(old_server_id) - old_bu_path = server_data['backup_path'] + old_bu_path = server_data["backup_path"] Server_Perms_Controller.backup_role_swap(old_server_id, new_server_id) if not helper.is_os_windows(): backup_path = helper.validate_traversal(helper.backup_path, old_bu_path) if helper.is_os_windows(): - backup_path = helper.validate_traversal(helper.wtol_path(helper.backup_path), helper.wtol_path(old_bu_path)) + backup_path = helper.validate_traversal( + helper.wtol_path(helper.backup_path), helper.wtol_path(old_bu_path) + ) backup_path = helper.wtol_path(str(backup_path)) - backup_path.replace(' ', '^ ') + backup_path.replace(" ", "^ ") backup_path = Path(backup_path) backup_path_components = list(backup_path.parts) backup_path_components[-1] = new_uuid @@ -567,27 +706,45 @@ class Controller: os.rmdir(new_bu_path) backup_path.rename(new_bu_path) - def register_server(self, name: str, - server_uuid: str, - server_dir: str, - backup_path: str, - server_command: str, - server_file: str, - server_log_file: str, - server_stop: str, - server_port: int, - server_type: str): + def register_server( + self, + name: str, + server_uuid: str, + server_dir: str, + backup_path: str, + server_command: str, + server_file: str, + server_log_file: str, + server_stop: str, + server_port: int, + server_type: str, + ): # put data in the db new_id = self.servers.create_server( - name, server_uuid, server_dir, backup_path, server_command, server_file, server_log_file, server_stop, server_type, server_port) + name, + server_uuid, + server_dir, + backup_path, + server_command, + server_file, + server_log_file, + server_stop, + server_type, + server_port, + ) if not helper.check_file_exists(os.path.join(server_dir, "crafty_managed.txt")): try: # place a file in the dir saying it's owned by crafty - with open(os.path.join(server_dir, "crafty_managed.txt"), 'w', encoding='utf-8') as f: + with open( + os.path.join(server_dir, "crafty_managed.txt"), + "w", + encoding="utf-8", + ) as f: f.write( - "The server is managed by Crafty Controller.\n Leave this directory/files alone please") + "The server is managed by Crafty Controller.\n Leave this directory/files alone please" + ) f.close() except Exception as e: @@ -604,28 +761,41 @@ class Controller: for s in self.servers_list: # if this is the droid... im mean server we are looking for... - if str(s['server_id']) == str(server_id): + if str(s["server_id"]) == str(server_id): server_data = self.get_server_data(server_id) - server_name = server_data['server_name'] + server_name = server_data["server_name"] logger.info(f"Deleting Server: ID {server_id} | Name: {server_name} ") console.info(f"Deleting Server: ID {server_id} | Name: {server_name} ") - srv_obj = s['server_obj'] + srv_obj = s["server_obj"] running = srv_obj.check_running() if running: self.stop_server(server_id) if files: try: - file_helper.del_dirs(helper.get_os_understandable_path(self.servers.get_server_data_by_id(server_id)['path'])) + file_helper.del_dirs( + helper.get_os_understandable_path( + self.servers.get_server_data_by_id(server_id)["path"] + ) + ) except Exception as e: - logger.error(f"Unable to delete server files for server with ID: {server_id} with error logged: {e}") - if helper.check_path_exists(self.servers.get_server_data_by_id(server_id)['backup_path']): - file_helper.del_dirs(helper.get_os_understandable_path(self.servers.get_server_data_by_id(server_id)['backup_path'])) + logger.error( + f"Unable to delete server files for server with ID: {server_id} with error logged: {e}" + ) + if helper.check_path_exists( + self.servers.get_server_data_by_id(server_id)["backup_path"] + ): + file_helper.del_dirs( + helper.get_os_understandable_path( + self.servers.get_server_data_by_id(server_id)[ + "backup_path" + ] + ) + ) - - #Cleanup scheduled tasks + # Cleanup scheduled tasks try: helpers_management.delete_scheduled_task_by_server(server_id) except DoesNotExist: @@ -637,6 +807,7 @@ class Controller: self.servers_list.pop(counter) counter += 1 + @staticmethod def clear_unexecuted_commands(): helpers_management.clear_unexecuted_commands() diff --git a/app/classes/shared/main_models.py b/app/classes/shared/main_models.py index eee69f82..b1a79bb9 100644 --- a/app/classes/shared/main_models.py +++ b/app/classes/shared/main_models.py @@ -17,24 +17,29 @@ except ModuleNotFoundError as err: helper.auto_installer_fix(err) logger = logging.getLogger(__name__) -peewee_logger = logging.getLogger('peewee') +peewee_logger = logging.getLogger("peewee") peewee_logger.setLevel(logging.INFO) -database = SqliteDatabase(helper.db_path, pragmas = { - 'journal_mode': 'wal', - 'cache_size': -1024 * 10}) +database = SqliteDatabase( + helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} +) + class db_builder: - @staticmethod def default_settings(): logger.info("Fresh Install Detected - Creating Default Settings") console.info("Fresh Install Detected - Creating Default Settings") default_data = helper.find_default_password() - username = default_data.get("username", 'admin') - password = default_data.get("password", 'crafty') + username = default_data.get("username", "admin") + password = default_data.get("password", "crafty") - users_helper.add_user(username=username, password=password, email="default@example.com", superuser=True) + users_helper.add_user( + username=username, + password=password, + email="default@example.com", + superuser=True, + ) @staticmethod def is_fresh_install(): @@ -45,11 +50,12 @@ class db_builder: except: return True + class db_shortcuts: - #************************************************************************************************ + # ************************************************************************************************ # Generic Databse Methods - #************************************************************************************************ + # ************************************************************************************************ @staticmethod def return_rows(query): rows = [] @@ -69,8 +75,8 @@ class db_shortcuts: return data -#************************************************************************************************ +# ************************************************************************************************ # Static Accessors -#************************************************************************************************ +# ************************************************************************************************ installer = db_builder() db_helper = db_shortcuts() diff --git a/app/classes/shared/migration.py b/app/classes/shared/migration.py index 4105b833..10781965 100644 --- a/app/classes/shared/migration.py +++ b/app/classes/shared/migration.py @@ -15,8 +15,10 @@ try: import peewee from playhouse.migrate import ( SqliteMigrator, - Operation, SQL, SqliteDatabase, - make_index_name + Operation, + SQL, + SqliteDatabase, + make_index_name, ) except ModuleNotFoundError as e: @@ -24,7 +26,7 @@ except ModuleNotFoundError as e: logger = logging.getLogger(__name__) -MIGRATE_TABLE = 'migratehistory' +MIGRATE_TABLE = "migratehistory" MIGRATE_TEMPLATE = '''# Generated by database migrator import peewee @@ -70,6 +72,7 @@ def get_model(method): if isinstance(model, str): return method(migrator, migrator.table_dict[model], *args, **kwargs) return method(migrator, model, *args, **kwargs) + return wrapper @@ -133,11 +136,17 @@ class Migrator(object): """ for name, field in fields.items(): model._meta.add_field(name, field) - self.operations.append(self.migrator.add_column( - model._meta.table_name, field.column_name, field)) + self.operations.append( + self.migrator.add_column( + model._meta.table_name, field.column_name, field + ) + ) if field.unique: - self.operations.append(self.migrator.add_index( - model._meta.table_name, (field.column_name,), unique=True)) + self.operations.append( + self.migrator.add_index( + model._meta.table_name, (field.column_name,), unique=True + ) + ) return model @get_model @@ -145,19 +154,22 @@ class Migrator(object): """ Removes fields from model. """ - fields = [field for field in model._meta.fields.values() - if field.name in names] + fields = [field for field in model._meta.fields.values() if field.name in names] for field in fields: self.__del_field__(model, field) if field.unique: # Drop unique index index_name = make_index_name( - model._meta.table_name, [field.column_name]) - self.operations.append(self.migrator.drop_index( - model._meta.table_name, index_name)) + model._meta.table_name, [field.column_name] + ) + self.operations.append( + self.migrator.drop_index(model._meta.table_name, index_name) + ) self.operations.append( self.migrator.drop_column( - model._meta.table_name, field.column_name, cascade=False)) + model._meta.table_name, field.column_name, cascade=False + ) + ) return model def __del_field__(self, model: peewee.Model, field: peewee.Field): @@ -169,12 +181,14 @@ class Migrator(object): if isinstance(field, peewee.ForeignKeyField): obj_id_name = field.column_name if field.column_name == field.name: - obj_id_name += '_id' + obj_id_name += "_id" delattr(model, obj_id_name) delattr(field.rel_model, field.backref) @get_model - def rename_column(self, model: peewee.Model, old_name: str, new_name: str) -> peewee.Model: + def rename_column( + self, model: peewee.Model, old_name: str, new_name: str + ) -> peewee.Model: """ Renames field in model. """ @@ -185,9 +199,10 @@ class Migrator(object): field.name = field.column_name = new_name model._meta.add_field(new_name, field) if isinstance(field, peewee.ForeignKeyField): - field.column_name = new_name = field.column_name + '_id' - self.operations.append(self.migrator.rename_column( - model._meta.table_name, old_name, new_name)) + field.column_name = new_name = field.column_name + "_id" + self.operations.append( + self.migrator.rename_column(model._meta.table_name, old_name, new_name) + ) return model @get_model @@ -203,7 +218,9 @@ class Migrator(object): return model @get_model - def add_index(self, model: peewee.Model, *columns: str, unique=False) -> peewee.Model: + def add_index( + self, model: peewee.Model, *columns: str, unique=False + ) -> peewee.Model: """Create indexes.""" model._meta.indexes.append((columns, unique)) columns_ = [] @@ -215,11 +232,12 @@ class Migrator(object): field.index = not unique if isinstance(field, peewee.ForeignKeyField): - col = col + '_id' + col = col + "_id" columns_.append(col) - self.operations.append(self.migrator.add_index( - model._meta.table_name, columns_, unique=unique)) + self.operations.append( + self.migrator.add_index(model._meta.table_name, columns_, unique=unique) + ) return model @get_model @@ -235,13 +253,15 @@ class Migrator(object): field.unique = field.index = False if isinstance(field, peewee.ForeignKeyField): - col = col + '_id' + col = col + "_id" columns_.append(col) index_name = make_index_name(model._meta.table_name, columns_) - model._meta.indexes = [(cols, _) for ( - cols, _) in model._meta.indexes if columns != cols] - self.operations.append(self.migrator.drop_index( - model._meta.table_name, index_name)) + model._meta.indexes = [ + (cols, _) for (cols, _) in model._meta.indexes if columns != cols + ] + self.operations.append( + self.migrator.drop_index(model._meta.table_name, index_name) + ) return model @get_model @@ -250,8 +270,9 @@ class Migrator(object): for name in names: field = model._meta.fields[name] field.null = False - self.operations.append(self.migrator.add_not_null( - model._meta.table_name, field.column_name)) + self.operations.append( + self.migrator.add_not_null(model._meta.table_name, field.column_name) + ) return model @get_model @@ -260,17 +281,21 @@ class Migrator(object): for name in names: field = model._meta.fields[name] field.null = True - self.operations.append(self.migrator.drop_not_null( - model._meta.table_name, field.column_name)) + self.operations.append( + self.migrator.drop_not_null(model._meta.table_name, field.column_name) + ) return model @get_model - def add_default(self, model: peewee.Model, name: str, default: t.Any) -> peewee.Model: + def add_default( + self, model: peewee.Model, name: str, default: t.Any + ) -> peewee.Model: """Add default.""" field = model._meta.fields[name] model._meta.defaults[field] = field.default = default - self.operations.append(self.migrator.apply_default( - model._meta.table_name, name, field)) + self.operations.append( + self.migrator.apply_default(model._meta.table_name, name, field) + ) return model @@ -283,7 +308,7 @@ class MigrationManager(object): Initializes the migration manager. """ if not isinstance(database, (peewee.Database, peewee.Proxy)): - raise RuntimeError('Invalid database: {}'.format(database)) + raise RuntimeError("Invalid database: {}".format(database)) self.database = database @cached_property @@ -292,7 +317,7 @@ class MigrationManager(object): Initialize and cache the MigrationHistory model. """ MigrateHistory._meta.database = self.database - MigrateHistory._meta.table_name = 'migratehistory' + MigrateHistory._meta.table_name = "migratehistory" MigrateHistory._meta.schema = None MigrateHistory.create_table(True) return MigrateHistory @@ -310,10 +335,13 @@ class MigrationManager(object): Scans migrations in the file system. """ if not os.path.exists(helper.migration_dir): - logger.warning('Migration directory: {} does not exist.'.format( - helper.migration_dir)) + logger.warning( + "Migration directory: {} does not exist.".format(helper.migration_dir) + ) os.makedirs(helper.migration_dir) - return sorted(f[:-3] for f in os.listdir(helper.migration_dir) if self.filemask.match(f)) + return sorted( + f[:-3] for f in os.listdir(helper.migration_dir) if self.filemask.match(f) + ) @property def diff(self) -> t.List[str]: @@ -333,24 +361,27 @@ class MigrationManager(object): self.up_one(name, migrator, True) return migrator - def compile(self, name, migrate='', rollback=''): + def compile(self, name, migrate="", rollback=""): """ Compiles a migration. """ - name = datetime.utcnow().strftime('%Y%m%d%H%M%S') + '_' + name - filename = name + '.py' + name = datetime.utcnow().strftime("%Y%m%d%H%M%S") + "_" + name + filename = name + ".py" path = os.path.join(helper.migration_dir, filename) - with open(path, 'w') as f: - f.write(MIGRATE_TEMPLATE.format( - migrate=migrate, rollback=rollback, name=filename)) + with open(path, "w") as f: + f.write( + MIGRATE_TEMPLATE.format( + migrate=migrate, rollback=rollback, name=filename + ) + ) return name - def create(self, name: str = 'auto', auto: bool = False) -> t.Optional[str]: + def create(self, name: str = "auto", auto: bool = False) -> t.Optional[str]: """ Creates a migration. """ - migrate = rollback = '' + migrate = rollback = "" if auto: raise NotImplementedError @@ -367,14 +398,14 @@ class MigrationManager(object): """ Runs all unapplied migrations. """ - logger.info('Starting migrations') - console.info('Starting migrations') + logger.info("Starting migrations") + console.info("Starting migrations") done = [] diff = self.diff if not diff: - logger.info('There is nothing to migrate') - console.info('There is nothing to migrate') + logger.info("There is nothing to migrate") + console.info("There is nothing to migrate") return done migrator = self.migrator @@ -392,16 +423,19 @@ class MigrationManager(object): call_params = dict() if helper.is_os_windows() and sys.version_info >= (3, 0): # if system is windows - force utf-8 encoding - call_params['encoding'] = 'utf-8' - with open(os.path.join(helper.migration_dir, name + '.py'), **call_params) as f: + call_params["encoding"] = "utf-8" + with open(os.path.join(helper.migration_dir, name + ".py"), **call_params) as f: code = f.read() scope = {} - code = compile(code, '', 'exec', dont_inherit=True) + code = compile(code, "", "exec", dont_inherit=True) exec(code, scope, None) - return scope.get('migrate', lambda m, d: None), scope.get('rollback', lambda m, d: None) + return scope.get("migrate", lambda m, d: None), scope.get( + "rollback", lambda m, d: None + ) - def up_one(self, name: str, migrator: Migrator, - fake: bool = False, rollback: bool = False) -> str: + def up_one( + self, name: str, migrator: Migrator, fake: bool = False, rollback: bool = False + ) -> str: """ Runs a migration with a given name. """ @@ -429,8 +463,8 @@ class MigrationManager(object): except Exception: self.database.rollback() - operation_name = 'Rollback' if rollback else 'Migration' - logger.exception('{} failed: {}'.format(operation_name, name)) + operation_name = "Rollback" if rollback else "Migration" + logger.exception("{} failed: {}".format(operation_name, name)) raise def down(self): @@ -438,10 +472,10 @@ class MigrationManager(object): Rolls back migrations. """ if not self.done: - raise RuntimeError('No migrations are found.') + raise RuntimeError("No migrations are found.") name = self.done[-1] migrator = self.migrator self.up_one(name, migrator, False, True) - logger.warning('Rolled back migration: {}'.format(name)) + logger.warning("Rolled back migration: {}".format(name)) diff --git a/app/classes/shared/permission_helper.py b/app/classes/shared/permission_helper.py index cd1ee52a..7874d792 100644 --- a/app/classes/shared/permission_helper.py +++ b/app/classes/shared/permission_helper.py @@ -1,22 +1,27 @@ from enum import Enum + class PermissionHelper: @staticmethod def both_have_perm(a: str, b: str, permission_tested: Enum): - return permission_helper.combine_perm_bool(a[permission_tested.value], b[permission_tested.value]) + return permission_helper.combine_perm_bool( + a[permission_tested.value], b[permission_tested.value] + ) @staticmethod def combine_perm(a: str, b: str) -> str: - return '1' if (a == '1' and b == '1') else '0' + return "1" if (a == "1" and b == "1") else "0" @staticmethod def combine_perm_bool(a: str, b: str) -> bool: - return a == '1' and b == '1' + return a == "1" and b == "1" @staticmethod def combine_masks(permission_mask_a: str, permission_mask_b: str) -> str: both_masks = zip(list(permission_mask_a), list(permission_mask_b)) - return ''.join(map(lambda x: permission_helper.combine_perm(x[0], x[1]), both_masks)) + return "".join( + map(lambda x: permission_helper.combine_perm(x[0], x[1]), both_masks) + ) permission_helper = PermissionHelper() diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 71f30f99..5c7541e2 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -23,7 +23,8 @@ from app.classes.web.websocket_helper import websocket_helper try: import psutil - #TZLocal is set as a hidden import on win pipeline + + # TZLocal is set as a hidden import on win pipeline from tzlocal import get_localzone from apscheduler.schedulers.background import BackgroundScheduler @@ -32,6 +33,7 @@ except ModuleNotFoundError as e: logger = logging.getLogger(__name__) + class ServerOutBuf: lines = {} @@ -39,8 +41,8 @@ class ServerOutBuf: self.proc = proc self.server_id = str(server_id) # Buffers text for virtual_terminal_lines config number of lines - self.max_lines = helper.get_setting('virtual_terminal_lines') - self.line_buffer = '' + self.max_lines = helper.get_setting("virtual_terminal_lines") + self.line_buffer = "" ServerOutBuf.lines[self.server_id] = [] self.lsi = 0 @@ -56,7 +58,7 @@ class ServerOutBuf: ServerOutBuf.lines[self.server_id].append(self.line_buffer) self.new_line_handler(self.line_buffer) - self.line_buffer = '' + self.line_buffer = "" # Limit list length to self.max_lines: if len(ServerOutBuf.lines[self.server_id]) > self.max_lines: ServerOutBuf.lines[self.server_id].pop(0) @@ -64,40 +66,35 @@ class ServerOutBuf: def check(self): while True: if self.proc.poll() is None: - char = self.proc.stdout.read(1).decode('utf-8', 'ignore') + char = self.proc.stdout.read(1).decode("utf-8", "ignore") # TODO: we may want to benchmark reading in blocks and userspace processing it later, reads are kind of expensive as a syscall self.process_byte(char) else: - flush = self.proc.stdout.read().decode('utf-8', 'ignore') + flush = self.proc.stdout.read().decode("utf-8", "ignore") for char in flush: self.process_byte(char) break def new_line_handler(self, new_line): - new_line = re.sub('(\033\\[(0;)?[0-9]*[A-z]?(;[0-9])?m?)', ' ', new_line) - new_line = re.sub('[A-z]{2}\b\b', '', new_line) + new_line = re.sub("(\033\\[(0;)?[0-9]*[A-z]?(;[0-9])?m?)", " ", new_line) + new_line = re.sub("[A-z]{2}\b\b", "", new_line) highlighted = helper.log_colors(html.escape(new_line)) - logger.debug('Broadcasting new virtual terminal line') + logger.debug("Broadcasting new virtual terminal line") # TODO: Do not send data to clients who do not have permission to view this server's console websocket_helper.broadcast_page_params( - '/panel/server_detail', - { - 'id': self.server_id - }, - 'vterm_new_line', - { - 'line': highlighted + '
            ' - } + "/panel/server_detail", + {"id": self.server_id}, + "vterm_new_line", + {"line": highlighted + "
            "}, ) -#************************************************************************************************ +# ************************************************************************************************ # Minecraft Server Class -#************************************************************************************************ +# ************************************************************************************************ class Server: - def __init__(self, stats): # holders for our process self.process = None @@ -117,26 +114,29 @@ class Server: tz = get_localzone() self.server_scheduler = BackgroundScheduler(timezone=str(tz)) self.server_scheduler.start() - self.backup_thread = threading.Thread(target=self.a_backup_server, daemon=True, name=f"backup_{self.name}") + self.backup_thread = threading.Thread( + target=self.a_backup_server, daemon=True, name=f"backup_{self.name}" + ) self.is_backingup = False - #Reset crash and update at initialization + # Reset crash and update at initialization servers_helper.server_crash_reset(self.server_id) servers_helper.set_update(self.server_id, False) - -#************************************************************************************************ -# Minecraft Server Management -#************************************************************************************************ + # ************************************************************************************************ + # Minecraft Server Management + # ************************************************************************************************ def reload_server_settings(self): server_data = servers_helper.get_server_data_by_id(self.server_id) self.settings = server_data def do_server_setup(self, server_data_obj): - serverId = server_data_obj['server_id'] - serverName = server_data_obj['server_name'] - autoStart = server_data_obj['auto_start'] + serverId = server_data_obj["server_id"] + serverName = server_data_obj["server_name"] + autoStart = server_data_obj["auto_start"] - logger.info(f'Creating Server object: {serverId} | Server Name: {serverName} | Auto Start: {autoStart}') + logger.info( + f"Creating Server object: {serverId} | Server Name: {serverName} | Auto Start: {autoStart}" + ) self.server_id = serverId self.name = serverName self.settings = server_data_obj @@ -145,18 +145,23 @@ class Server: # build our server run command - if server_data_obj['auto_start']: - delay = int(self.settings['auto_start_delay']) + if server_data_obj["auto_start"]: + delay = int(self.settings["auto_start_delay"]) logger.info(f"Scheduling server {self.name} to start in {delay} seconds") console.info(f"Scheduling server {self.name} to start in {delay} seconds") - self.server_scheduler.add_job(self.run_scheduled_server, 'interval', seconds=delay, id=str(self.server_id)) + self.server_scheduler.add_job( + self.run_scheduled_server, + "interval", + seconds=delay, + id=str(self.server_id), + ) def run_scheduled_server(self): console.info(f"Starting server ID: {self.server_id} - {self.name}") logger.info(f"Starting server ID: {self.server_id} - {self.name}") - #Sets waiting start to false since we're attempting to start the server. + # Sets waiting start to false since we're attempting to start the server. servers_helper.set_waiting_start(self.server_id, False) self.run_threaded_server(None) @@ -165,30 +170,50 @@ class Server: def run_threaded_server(self, user_id): # start the server - self.server_thread = threading.Thread(target=self.start_server, daemon=True, args=(user_id,), name=f'{self.server_id}_server_thread') + self.server_thread = threading.Thread( + target=self.start_server, + daemon=True, + args=(user_id,), + name=f"{self.server_id}_server_thread", + ) self.server_thread.start() - #Register an shedule for polling server stats when running + # Register an shedule for polling server stats when running logger.info(f"Polling server statistics {self.name} every {5} seconds") console.info(f"Polling server statistics {self.name} every {5} seconds") try: - self.server_scheduler.add_job(self.realtime_stats, 'interval', seconds=5, id="stats_"+str(self.server_id)) + self.server_scheduler.add_job( + self.realtime_stats, + "interval", + seconds=5, + id="stats_" + str(self.server_id), + ) except: - self.server_scheduler.remove_job('stats_'+str(self.server_id)) - self.server_scheduler.add_job(self.realtime_stats, 'interval', seconds=5, id="stats_"+str(self.server_id)) - + self.server_scheduler.remove_job("stats_" + str(self.server_id)) + self.server_scheduler.add_job( + self.realtime_stats, + "interval", + seconds=5, + id="stats_" + str(self.server_id), + ) def setup_server_run_command(self): # configure the server - server_exec_path = helper.get_os_understandable_path(self.settings['executable']) - self.server_command = helper.cmdparse(self.settings['execution_command']) - self.server_path = helper.get_os_understandable_path(self.settings['path']) + server_exec_path = helper.get_os_understandable_path( + self.settings["executable"] + ) + self.server_command = helper.cmdparse(self.settings["execution_command"]) + self.server_path = helper.get_os_understandable_path(self.settings["path"]) # let's do some quick checking to make sure things actually exists full_path = os.path.join(self.server_path, server_exec_path) if not helper.check_file_exists(full_path): - logger.critical(f"Server executable path: {full_path} does not seem to exist") - console.critical(f"Server executable path: {full_path} does not seem to exist") + logger.critical( + f"Server executable path: {full_path} does not seem to exist" + ) + console.critical( + f"Server executable path: {full_path} does not seem to exist" + ) if not helper.check_path_exists(self.server_path): logger.critical(f"Server path: {self.server_path} does not seem to exits") @@ -200,18 +225,26 @@ class Server: def start_server(self, user_id): if not user_id: - user_lang = helper.get_setting('language') + user_lang = helper.get_setting("language") else: user_lang = users_helper.get_user_lang_by_id(user_id) if servers_helper.get_download_status(self.server_id): if user_id: - websocket_helper.broadcast_user(user_id, 'send_start_error',{ - 'error': translation.translate('error', 'not-downloaded', user_lang) - }) + websocket_helper.broadcast_user( + user_id, + "send_start_error", + { + "error": translation.translate( + "error", "not-downloaded", user_lang + ) + }, + ) return False - logger.info(f"Start command detected. Reloading settings from DB for server {self.name}") + logger.info( + f"Start command detected. Reloading settings from DB for server {self.name}" + ) self.setup_server_run_command() # fail safe in case we try to start something already running if self.check_running(): @@ -225,21 +258,23 @@ class Server: logger.info(f"Launching Server {self.name} with command {self.server_command}") console.info(f"Launching Server {self.name} with command {self.server_command}") - #Checks for eula. Creates one if none detected. - #If EULA is detected and not set to one of these true vaiants we offer to set it true. - if helper.check_file_exists(os.path.join(self.settings['path'], 'eula.txt')): - f = open(os.path.join(self.settings['path'], 'eula.txt'), 'r', encoding='utf-8') + # Checks for eula. Creates one if none detected. + # If EULA is detected and not set to one of these true vaiants we offer to set it true. + if helper.check_file_exists(os.path.join(self.settings["path"], "eula.txt")): + f = open( + os.path.join(self.settings["path"], "eula.txt"), "r", encoding="utf-8" + ) line = f.readline().lower() - if line == 'eula=true': + if line == "eula=true": e_flag = True - elif line == 'eula = true': + elif line == "eula = true": e_flag = True - elif line == 'eula= true': + elif line == "eula= true": e_flag = True - elif line == 'eula =true': + elif line == "eula =true": e_flag = True else: @@ -249,11 +284,13 @@ class Server: if not e_flag: if user_id: - websocket_helper.broadcast_user(user_id, 'send_eula_bootbox', { - 'id': self.server_id - }) + websocket_helper.broadcast_user( + user_id, "send_eula_bootbox", {"id": self.server_id} + ) else: - logger.error("Autostart failed due to EULA being false. Agree not sent due to auto start.") + logger.error( + "Autostart failed due to EULA being false. Agree not sent due to auto start." + ) return False return False f.close() @@ -262,64 +299,120 @@ class Server: else: logger.info("Unix Detected") - logger.info(f"Starting server in {self.server_path} with command: {self.server_command}") + logger.info( + f"Starting server in {self.server_path} with command: {self.server_command}" + ) - #checks to make sure file is openable (downloaded) and exists. + # checks to make sure file is openable (downloaded) and exists. try: - f = open(os.path.join(self.server_path, servers_helper.get_server_data_by_id(self.server_id)['executable']), "r", encoding="utf-8") + f = open( + os.path.join( + self.server_path, + servers_helper.get_server_data_by_id(self.server_id)["executable"], + ), + "r", + encoding="utf-8", + ) f.close() except: if user_id: - websocket_helper.broadcast_user(user_id, 'send_start_error',{ - 'error': translation.translate('error', 'not-downloaded', user_lang) - }) + websocket_helper.broadcast_user( + user_id, + "send_start_error", + { + "error": translation.translate( + "error", "not-downloaded", user_lang + ) + }, + ) return - if not helper.is_os_windows() and servers_helper.get_server_type_by_id(self.server_id) == "minecraft-bedrock": - logger.info(f"Bedrock and Unix detected for server {self.name}. Switching to appropriate execution string") + if ( + not helper.is_os_windows() + and servers_helper.get_server_type_by_id(self.server_id) + == "minecraft-bedrock" + ): + logger.info( + f"Bedrock and Unix detected for server {self.name}. Switching to appropriate execution string" + ) my_env = os.environ my_env["LD_LIBRARY_PATH"] = self.server_path try: self.process = subprocess.Popen( - self.server_command, cwd=self.server_path, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=my_env) + self.server_command, + cwd=self.server_path, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=my_env, + ) except Exception as ex: - logger.error(f"Server {self.name} failed to start with error code: {ex}") + logger.error( + f"Server {self.name} failed to start with error code: {ex}" + ) if user_id: - websocket_helper.broadcast_user(user_id, 'send_start_error',{ - 'error': translation.translate('error', 'start-error', user_lang).format(self.name, ex) - }) + websocket_helper.broadcast_user( + user_id, + "send_start_error", + { + "error": translation.translate( + "error", "start-error", user_lang + ).format(self.name, ex) + }, + ) return False else: try: self.process = subprocess.Popen( - self.server_command, cwd=self.server_path, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + self.server_command, + cwd=self.server_path, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) except Exception as ex: - #Checks for java on initial fail + # Checks for java on initial fail if os.system("java -version") == 32512: if user_id: - websocket_helper.broadcast_user(user_id, 'send_start_error',{ - 'error': translation.translate('error', 'noJava', user_lang).format(self.name) - }) + websocket_helper.broadcast_user( + user_id, + "send_start_error", + { + "error": translation.translate( + "error", "noJava", user_lang + ).format(self.name) + }, + ) return False else: - logger.error(f"Server {self.name} failed to start with error code: {ex}") + logger.error( + f"Server {self.name} failed to start with error code: {ex}" + ) if user_id: - websocket_helper.broadcast_user(user_id, 'send_start_error',{ - 'error': translation.translate('error', 'start-error', user_lang).format(self.name, ex) - }) + websocket_helper.broadcast_user( + user_id, + "send_start_error", + { + "error": translation.translate( + "error", "start-error", user_lang + ).format(self.name, ex) + }, + ) return False out_buf = ServerOutBuf(self.process, self.server_id) - logger.debug(f'Starting virtual terminal listener for server {self.name}') - threading.Thread(target=out_buf.check, daemon=True, name=f'{self.server_id}_virtual_terminal').start() + logger.debug(f"Starting virtual terminal listener for server {self.name}") + threading.Thread( + target=out_buf.check, daemon=True, name=f"{self.server_id}_virtual_terminal" + ).start() self.is_crashed = False servers_helper.server_crash_reset(self.server_id) - self.start_time = str(datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')) + self.start_time = str(datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")) if self.process.poll() is None: logger.info(f"Server {self.name} running with PID {self.process.pid}") @@ -328,58 +421,91 @@ class Server: servers_helper.server_crash_reset(self.server_id) self.record_server_stats() check_internet_thread = threading.Thread( - target=self.check_internet_thread, daemon=True, args=(user_id, user_lang, ), name=f"{self.name}_Internet") + target=self.check_internet_thread, + daemon=True, + args=( + user_id, + user_lang, + ), + name=f"{self.name}_Internet", + ) check_internet_thread.start() - #Checks if this is the servers first run. + # Checks if this is the servers first run. if servers_helper.get_first_run(self.server_id): servers_helper.set_first_run(self.server_id) - loc_server_port = servers_helper.get_server_stats_by_id(self.server_id)['server_port'] - #Sends port reminder message. - websocket_helper.broadcast_user(user_id, 'send_start_error', { - 'error': translation.translate('error', 'portReminder', user_lang).format(self.name, loc_server_port) - }) + loc_server_port = servers_helper.get_server_stats_by_id(self.server_id)[ + "server_port" + ] + # Sends port reminder message. + websocket_helper.broadcast_user( + user_id, + "send_start_error", + { + "error": translation.translate( + "error", "portReminder", user_lang + ).format(self.name, loc_server_port) + }, + ) server_users = server_permissions.get_server_user_list(self.server_id) for user in server_users: if user != user_id: - websocket_helper.broadcast_user(user, 'send_start_reload', { - }) + websocket_helper.broadcast_user(user, "send_start_reload", {}) else: server_users = server_permissions.get_server_user_list(self.server_id) for user in server_users: - websocket_helper.broadcast_user(user, 'send_start_reload', { - }) + websocket_helper.broadcast_user(user, "send_start_reload", {}) else: - logger.warning(f"Server PID {self.process.pid} died right after starting - is this a server config issue?") - console.warning(f"Server PID {self.process.pid} died right after starting - is this a server config issue?") + logger.warning( + f"Server PID {self.process.pid} died right after starting - is this a server config issue?" + ) + console.warning( + f"Server PID {self.process.pid} died right after starting - is this a server config issue?" + ) - if self.settings['crash_detection']: - logger.info(f"Server {self.name} has crash detection enabled - starting watcher task") - console.info(f"Server {self.name} has crash detection enabled - starting watcher task") + if self.settings["crash_detection"]: + logger.info( + f"Server {self.name} has crash detection enabled - starting watcher task" + ) + console.info( + f"Server {self.name} has crash detection enabled - starting watcher task" + ) - self.server_scheduler.add_job(self.detect_crash, 'interval', seconds=30, id=f"c_{self.server_id}") + self.server_scheduler.add_job( + self.detect_crash, "interval", seconds=30, id=f"c_{self.server_id}" + ) def check_internet_thread(self, user_id, user_lang): if user_id: if not helper.check_internet(): - websocket_helper.broadcast_user(user_id, 'send_start_error', { - 'error': translation.translate('error', 'internet', user_lang) - }) + websocket_helper.broadcast_user( + user_id, + "send_start_error", + {"error": translation.translate("error", "internet", user_lang)}, + ) def stop_crash_detection(self): - #This is only used if the crash detection settings change while the server is running. + # This is only used if the crash detection settings change while the server is running. if self.check_running(): logger.info(f"Detected crash detection shut off for server {self.name}") try: - self.server_scheduler.remove_job('c_' + str(self.server_id)) + self.server_scheduler.remove_job("c_" + str(self.server_id)) except: - logger.error(f"Removing crash watcher for server {self.name} failed. Assuming it was never started.") + logger.error( + f"Removing crash watcher for server {self.name} failed. Assuming it was never started." + ) def start_crash_detection(self): - #This is only used if the crash detection settings change while the server is running. + # This is only used if the crash detection settings change while the server is running. if self.check_running(): - logger.info(f"Server {self.name} has crash detection enabled - starting watcher task") - console.info(f"Server {self.name} has crash detection enabled - starting watcher task") - self.server_scheduler.add_job(self.detect_crash, 'interval', seconds=30, id=f"c_{self.server_id}") + logger.info( + f"Server {self.name} has crash detection enabled - starting watcher task" + ) + console.info( + f"Server {self.name} has crash detection enabled - starting watcher task" + ) + self.server_scheduler.add_job( + self.detect_crash, "interval", seconds=30, id=f"c_{self.server_id}" + ) def stop_threaded_server(self): self.stop_server() @@ -388,17 +514,19 @@ class Server: self.server_thread.join() def stop_server(self): - if self.settings['stop_command']: - self.send_command(self.settings['stop_command']) - if self.settings['crash_detection']: - #remove crash detection watcher + if self.settings["stop_command"]: + self.send_command(self.settings["stop_command"]) + if self.settings["crash_detection"]: + # remove crash detection watcher logger.info(f"Removing crash watcher for server {self.name}") try: - self.server_scheduler.remove_job('c_' + str(self.server_id)) + self.server_scheduler.remove_job("c_" + str(self.server_id)) except: - logger.error(f"Removing crash watcher for server {self.name} failed. Assuming it was never started.") + logger.error( + f"Removing crash watcher for server {self.name} failed. Assuming it was never started." + ) else: - #windows will need to be handled separately for Ctrl+C + # windows will need to be handled separately for Ctrl+C self.process.terminate() running = self.check_running() if not running: @@ -411,9 +539,8 @@ class Server: server_name = self.name server_pid = self.process.pid - while running: - x = x+1 + x = x + 1 logstr = f"Server {server_name} is still running - waiting 2s to see if it stops ({int(60-(x*2))} seconds until force close)" logger.info(logstr) console.info(logstr) @@ -422,8 +549,12 @@ class Server: # if we haven't closed in 60 seconds, let's just slam down on the PID if x >= 30: - logger.info(f"Server {server_name} is still running - Forcing the process down") - console.info(f"Server {server_name} is still running - Forcing the process down") + logger.info( + f"Server {server_name} is still running - Forcing the process down" + ) + console.info( + f"Server {server_name} is still running - Forcing the process down" + ) self.kill() logger.info(f"Stopped Server {server_name} with PID {server_pid}") @@ -434,13 +565,12 @@ class Server: server_users = server_permissions.get_server_user_list(self.server_id) # remove the stats polling job since server is stopped - self.server_scheduler.remove_job("stats_"+str(self.server_id)) + self.server_scheduler.remove_job("stats_" + str(self.server_id)) self.record_server_stats() for user in server_users: - websocket_helper.broadcast_user(user, 'send_start_reload', { - }) + websocket_helper.broadcast_user(user, "send_start_reload", {}) def restart_threaded_server(self, user_id): # if not already running, let's just start @@ -470,14 +600,14 @@ class Server: return False def send_command(self, command): - if not self.check_running() and command.lower() != 'start': - logger.warning(f"Server not running, unable to send command \"{command}\"") + if not self.check_running() and command.lower() != "start": + logger.warning(f'Server not running, unable to send command "{command}"') return False console.info(f"COMMAND TIME: {command}") logger.debug(f"Sending command {command} to server") # send it - self.process.stdin.write(f"{command}\n".encode('utf-8')) + self.process.stdin.write(f"{command}\n".encode("utf-8")) self.process.stdin.flush() def crash_detected(self, name): @@ -485,19 +615,29 @@ class Server: # clear the old scheduled watcher task self.server_scheduler.remove_job(f"c_{self.server_id}") # remove the stats polling job since server is stopped - self.server_scheduler.remove_job("stats_"+str(self.server_id)) + self.server_scheduler.remove_job("stats_" + str(self.server_id)) # the server crashed, or isn't found - so let's reset things. - logger.warning(f"The server {name} seems to have vanished unexpectedly, did it crash?") + logger.warning( + f"The server {name} seems to have vanished unexpectedly, did it crash?" + ) - if self.settings['crash_detection']: - logger.warning(f"The server {name} has crashed and will be restarted. Restarting server") - console.warning(f"The server {name} has crashed and will be restarted. Restarting server") + if self.settings["crash_detection"]: + logger.warning( + f"The server {name} has crashed and will be restarted. Restarting server" + ) + console.warning( + f"The server {name} has crashed and will be restarted. Restarting server" + ) self.run_threaded_server(None) return True else: - logger.critical(f"The server {name} has crashed, crash detection is disabled and it will not be restarted") - console.critical(f"The server {name} has crashed, crash detection is disabled and it will not be restarted") + logger.critical( + f"The server {name} has crashed, crash detection is disabled and it will not be restarted" + ) + console.critical( + f"The server {name} has crashed, crash detection is disabled and it will not be restarted" + ) return False def kill(self): @@ -510,8 +650,8 @@ class Server: logger.info(f"Sending SIGKILL to server {proc.name}") proc.kill() # kill the main process we are after - logger.info('Sending SIGKILL to parent') - self.server_scheduler.remove_job("stats_"+str(self.server_id)) + logger.info("Sending SIGKILL to parent") + self.server_scheduler.remove_job("stats_" + str(self.server_id)) self.process.kill() def get_start_time(self): @@ -535,12 +675,14 @@ class Server: # if all is okay, we just exit out if running: return - #check the exit code -- This could be a fix for /stop + # check the exit code -- This could be a fix for /stop if self.process.returncode == 0: - logger.warning(f'Process {self.process.pid} exited with code {self.process.returncode}. This is considered a clean exit'+ - ' supressing crash handling.') + logger.warning( + f"Process {self.process.pid} exited with code {self.process.returncode}. This is considered a clean exit" + + " supressing crash handling." + ) # cancel the watcher task - self.server_scheduler.remove_job("c_"+str(self.server_id)) + self.server_scheduler.remove_job("c_" + str(self.server_id)) return servers_helper.sever_crashed(self.server_id) @@ -556,25 +698,29 @@ class Server: # we have tried to restart 4 times... elif self.restart_count == 4: - logger.critical(f"Server {self.name} has been restarted {self.restart_count} times. It has crashed, not restarting.") - console.critical(f"Server {self.name} has been restarted {self.restart_count} times. It has crashed, not restarting.") + logger.critical( + f"Server {self.name} has been restarted {self.restart_count} times. It has crashed, not restarting." + ) + console.critical( + f"Server {self.name} has been restarted {self.restart_count} times. It has crashed, not restarting." + ) self.restart_count = 0 self.is_crashed = True servers_helper.sever_crashed(self.server_id) # cancel the watcher task - self.server_scheduler.remove_job("c_"+str(self.server_id)) + self.server_scheduler.remove_job("c_" + str(self.server_id)) def remove_watcher_thread(self): logger.info("Removing old crash detection watcher thread") console.info("Removing old crash detection watcher thread") - self.server_scheduler.remove_job('c_'+str(self.server_id)) + self.server_scheduler.remove_job("c_" + str(self.server_id)) def agree_eula(self, user_id): - file = os.path.join(self.server_path, 'eula.txt') - f = open(file, 'w', encoding='utf-8') - f.write('eula=true') + file = os.path.join(self.server_path, "eula.txt") + f = open(file, "w", encoding="utf-8") + f.write("eula=true") f.close() self.run_threaded_server(user_id) @@ -585,12 +731,18 @@ class Server: return False def backup_server(self): - backup_thread = threading.Thread(target=self.a_backup_server, daemon=True, name=f"backup_{self.name}") - logger.info(f"Starting Backup Thread for server {self.settings['server_name']}.") + backup_thread = threading.Thread( + target=self.a_backup_server, daemon=True, name=f"backup_{self.name}" + ) + logger.info( + f"Starting Backup Thread for server {self.settings['server_name']}." + ) if self.server_path is None: - self.server_path = helper.get_os_understandable_path(self.settings['path']) - logger.info("Backup Thread - Local server path not defined. Setting local server path variable.") - #checks if the backup thread is currently alive for this server + self.server_path = helper.get_os_understandable_path(self.settings["path"]) + logger.info( + "Backup Thread - Local server path not defined. Setting local server path variable." + ) + # checks if the backup thread is currently alive for this server if not self.is_backingup: try: backup_thread.start() @@ -599,48 +751,59 @@ class Server: logger.error(f"Failed to start backup: {ex}") return False else: - logger.error(f"Backup is already being processed for server {self.settings['server_name']}. Canceling backup request") + logger.error( + f"Backup is already being processed for server {self.settings['server_name']}. Canceling backup request" + ) return False logger.info(f"Backup Thread started for server {self.settings['server_name']}.") def a_backup_server(self): if len(websocket_helper.clients) > 0: websocket_helper.broadcast_page_params( - '/panel/server_detail', - { - 'id': str(self.server_id) - }, - 'backup_reload', - { - "percent": 0, - "total_files": 0 - } - ) + "/panel/server_detail", + {"id": str(self.server_id)}, + "backup_reload", + {"percent": 0, "total_files": 0}, + ) logger.info(f"Starting server {self.name} (ID {self.server_id}) backup") server_users = server_permissions.get_server_user_list(self.server_id) for user in server_users: - websocket_helper.broadcast_user(user, 'notification', translation.translate('notify', - 'backupStarted', users_helper.get_user_lang_by_id(user)).format(self.name)) + websocket_helper.broadcast_user( + user, + "notification", + translation.translate( + "notify", "backupStarted", users_helper.get_user_lang_by_id(user) + ).format(self.name), + ) time.sleep(3) conf = management_helper.get_backup_config(self.server_id) - helper.ensure_dir_exists(self.settings['backup_path']) + helper.ensure_dir_exists(self.settings["backup_path"]) try: backup_filename = f"{self.settings['backup_path']}/{datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}" - logger.info(f"Creating backup of server '{self.settings['server_name']}'" + - f" (ID#{self.server_id}, path={self.server_path}) at '{backup_filename}'") + logger.info( + f"Creating backup of server '{self.settings['server_name']}'" + + f" (ID#{self.server_id}, path={self.server_path}) at '{backup_filename}'" + ) tempDir = tempfile.mkdtemp() - self.server_scheduler.add_job(self.backup_status, 'interval', seconds=1, id="backup_"+str(self.server_id), args = [tempDir+'/', - backup_filename+'.zip']) + self.server_scheduler.add_job( + self.backup_status, + "interval", + seconds=1, + id="backup_" + str(self.server_id), + args=[tempDir + "/", backup_filename + ".zip"], + ) # pylint: disable=unexpected-keyword-arg file_helper.copy_dir(self.server_path, tempDir, dirs_exist_ok=True) excluded_dirs = management_helper.get_excluded_backup_dirs(self.server_id) - server_dir = helper.get_os_understandable_path(self.settings['path']) + server_dir = helper.get_os_understandable_path(self.settings["path"]) for my_dir in excluded_dirs: # Take the full path of the excluded dir and replace the server path with the temp path # This is so that we're only deleting excluded dirs from the temp path and not the server path - excluded_dir = helper.get_os_understandable_path(my_dir).replace(server_dir, helper.get_os_understandable_path(tempDir)) + excluded_dir = helper.get_os_understandable_path(my_dir).replace( + server_dir, helper.get_os_understandable_path(tempDir) + ) # Next, check to see if it is a directory if os.path.isdir(excluded_dir): # If it is a directory, recursively delete the entire directory from the backup @@ -648,14 +811,25 @@ class Server: else: # If not, just remove the file os.remove(excluded_dir) - if conf['compress']: - logger.debug("Found compress backup to be true. Calling compressed archive") - file_helper.make_compressed_archive(helper.get_os_understandable_path(backup_filename), tempDir) + if conf["compress"]: + logger.debug( + "Found compress backup to be true. Calling compressed archive" + ) + file_helper.make_compressed_archive( + helper.get_os_understandable_path(backup_filename), tempDir + ) else: - logger.debug("Found compress backup to be false. Calling NON-compressed archive") - file_helper.make_archive(helper.get_os_understandable_path(backup_filename), tempDir) + logger.debug( + "Found compress backup to be false. Calling NON-compressed archive" + ) + file_helper.make_archive( + helper.get_os_understandable_path(backup_filename), tempDir + ) - while len(self.list_backups()) > conf["max_backups"] and conf["max_backups"] > 0: + while ( + len(self.list_backups()) > conf["max_backups"] + and conf["max_backups"] > 0 + ): backup_list = self.list_backups() oldfile = backup_list[0] oldfile_path = f"{conf['backup_path']}/{oldfile['path']}" @@ -665,44 +839,41 @@ class Server: self.is_backingup = False file_helper.del_dirs(tempDir) logger.info(f"Backup of server: {self.name} completed") - self.server_scheduler.remove_job("backup_"+str(self.server_id)) - results = { - "percent": 100, - "total_files": 0, - "current_file": 0 - } + self.server_scheduler.remove_job("backup_" + str(self.server_id)) + results = {"percent": 100, "total_files": 0, "current_file": 0} if len(websocket_helper.clients) > 0: websocket_helper.broadcast_page_params( - '/panel/server_detail', - { - 'id': str(self.server_id) - }, - 'backup_status', - results - ) + "/panel/server_detail", + {"id": str(self.server_id)}, + "backup_status", + results, + ) server_users = server_permissions.get_server_user_list(self.server_id) for user in server_users: - websocket_helper.broadcast_user(user, 'notification', translation.translate('notify', 'backupComplete', - users_helper.get_user_lang_by_id(user)).format(self.name)) + websocket_helper.broadcast_user( + user, + "notification", + translation.translate( + "notify", + "backupComplete", + users_helper.get_user_lang_by_id(user), + ).format(self.name), + ) time.sleep(3) return except: - logger.exception(f"Failed to create backup of server {self.name} (ID {self.server_id})") - self.server_scheduler.remove_job("backup_"+str(self.server_id)) - results = { - "percent": 100, - "total_files": 0, - "current_file": 0 - } + logger.exception( + f"Failed to create backup of server {self.name} (ID {self.server_id})" + ) + self.server_scheduler.remove_job("backup_" + str(self.server_id)) + results = {"percent": 100, "total_files": 0, "current_file": 0} if len(websocket_helper.clients) > 0: websocket_helper.broadcast_page_params( - '/panel/server_detail', - { - 'id': str(self.server_id) - }, - 'backup_status', - results - ) + "/panel/server_detail", + {"id": str(self.server_id)}, + "backup_status", + results, + ) self.is_backingup = False return @@ -711,47 +882,58 @@ class Server: self.backup_stats = results if len(websocket_helper.clients) > 0: websocket_helper.broadcast_page_params( - '/panel/server_detail', - { - 'id': str(self.server_id) - }, - 'backup_status', - results - ) + "/panel/server_detail", + {"id": str(self.server_id)}, + "backup_status", + results, + ) def send_backup_status(self): try: return self.backup_stats except: - return { - 'percent': 0, - 'total_files': 0 - } + return {"percent": 0, "total_files": 0} def list_backups(self): - if self.settings['backup_path']: - if helper.check_path_exists(helper.get_os_understandable_path(self.settings['backup_path'])): - files = ( - helper.get_human_readable_files_sizes(helper.list_dir_by_date(helper.get_os_understandable_path(self.settings['backup_path'])))) - return [{ - "path": os.path.relpath(f['path'], - start=helper.get_os_understandable_path(self.settings['backup_path'])), - "size": f["size"] - } for f in files] + if self.settings["backup_path"]: + if helper.check_path_exists( + helper.get_os_understandable_path(self.settings["backup_path"]) + ): + files = helper.get_human_readable_files_sizes( + helper.list_dir_by_date( + helper.get_os_understandable_path(self.settings["backup_path"]) + ) + ) + return [ + { + "path": os.path.relpath( + f["path"], + start=helper.get_os_understandable_path( + self.settings["backup_path"] + ), + ), + "size": f["size"], + } + for f in files + ] else: return [] else: - logger.info(f"Error putting backup file list for server with ID: {self.server_id}") - return[] + logger.info( + f"Error putting backup file list for server with ID: {self.server_id}" + ) + return [] def jar_update(self): servers_helper.set_update(self.server_id, True) - update_thread = threading.Thread(target=self.a_jar_update, daemon=True, name=f"exe_update_{self.name}") + update_thread = threading.Thread( + target=self.a_jar_update, daemon=True, name=f"exe_update_{self.name}" + ) update_thread.start() def check_update(self): - if servers_helper.get_server_stats_by_id(self.server_id)['updating']: + if servers_helper.get_server_stats_by_id(self.server_id)["updating"]: return True else: return False @@ -759,51 +941,68 @@ class Server: def a_jar_update(self): wasStarted = "-1" self.backup_server() - #checks if server is running. Calls shutdown if it is running. + # checks if server is running. Calls shutdown if it is running. if self.check_running(): wasStarted = True - logger.info(f"Server with PID {self.process.pid} is running. Sending shutdown command") + logger.info( + f"Server with PID {self.process.pid} is running. Sending shutdown command" + ) self.stop_threaded_server() else: wasStarted = False if len(websocket_helper.clients) > 0: # There are clients self.check_update() - message = ' UPDATING...' - websocket_helper.broadcast_page('/panel/server_detail', 'update_button_status', { - 'isUpdating': self.check_update(), - 'server_id': self.server_id, - 'wasRunning': wasStarted, - 'string': message - }) - websocket_helper.broadcast_page('/panel/dashboard', 'send_start_reload', { - }) - backup_dir = os.path.join(helper.get_os_understandable_path(self.settings['path']), 'crafty_executable_backups') - #checks if backup directory already exists + message = ( + ' UPDATING...' + ) + websocket_helper.broadcast_page( + "/panel/server_detail", + "update_button_status", + { + "isUpdating": self.check_update(), + "server_id": self.server_id, + "wasRunning": wasStarted, + "string": message, + }, + ) + websocket_helper.broadcast_page("/panel/dashboard", "send_start_reload", {}) + backup_dir = os.path.join( + helper.get_os_understandable_path(self.settings["path"]), + "crafty_executable_backups", + ) + # checks if backup directory already exists if os.path.isdir(backup_dir): - backup_executable = os.path.join(backup_dir, 'old_server.jar') + backup_executable = os.path.join(backup_dir, "old_server.jar") else: - logger.info(f"Executable backup directory not found for Server: {self.name}. Creating one.") + logger.info( + f"Executable backup directory not found for Server: {self.name}. Creating one." + ) os.mkdir(backup_dir) - backup_executable = os.path.join(backup_dir, 'old_server.jar') + backup_executable = os.path.join(backup_dir, "old_server.jar") if os.path.isfile(backup_executable): - #removes old backup + # removes old backup logger.info(f"Old backup found for server: {self.name}. Removing...") os.remove(backup_executable) logger.info(f"Old backup removed for server: {self.name}.") else: logger.info(f"No old backups found for server: {self.name}") - current_executable = os.path.join(helper.get_os_understandable_path(self.settings['path']), self.settings['executable']) + current_executable = os.path.join( + helper.get_os_understandable_path(self.settings["path"]), + self.settings["executable"], + ) - #copies to backup dir + # copies to backup dir helper.copy_files(current_executable, backup_executable) - #boolean returns true for false for success - downloaded = helper.download_file(self.settings['executable_update_url'], current_executable) + # boolean returns true for false for success + downloaded = helper.download_file( + self.settings["executable_update_url"], current_executable + ) - while servers_helper.get_server_stats_by_id(self.server_id)['updating']: + while servers_helper.get_server_stats_by_id(self.server_id)["updating"]: if downloaded and not self.is_backingup: logger.info("Executable updated successfully. Starting Server") @@ -811,38 +1010,61 @@ class Server: if len(websocket_helper.clients) > 0: # There are clients self.check_update() - server_users = server_permissions.get_server_user_list(self.server_id) + server_users = server_permissions.get_server_user_list( + self.server_id + ) for user in server_users: - websocket_helper.broadcast_user(user, 'notification', "Executable update finished for " + self.name) + websocket_helper.broadcast_user( + user, + "notification", + "Executable update finished for " + self.name, + ) time.sleep(3) - websocket_helper.broadcast_page('/panel/server_detail', 'update_button_status', { - 'isUpdating': self.check_update(), - 'server_id': self.server_id, - 'wasRunning': wasStarted - }) - websocket_helper.broadcast_page('/panel/dashboard', 'send_start_reload', { - }) + websocket_helper.broadcast_page( + "/panel/server_detail", + "update_button_status", + { + "isUpdating": self.check_update(), + "server_id": self.server_id, + "wasRunning": wasStarted, + }, + ) + websocket_helper.broadcast_page( + "/panel/dashboard", "send_start_reload", {} + ) server_users = server_permissions.get_server_user_list(self.server_id) for user in server_users: - websocket_helper.broadcast_user(user, 'notification', "Executable update finished for "+self.name) + websocket_helper.broadcast_user( + user, + "notification", + "Executable update finished for " + self.name, + ) management_helper.add_to_audit_log_raw( - 'Alert', '-1', self.server_id, "Executable update finished for "+self.name, self.settings['server_ip']) + "Alert", + "-1", + self.server_id, + "Executable update finished for " + self.name, + self.settings["server_ip"], + ) if wasStarted: self.start_server() elif not downloaded and not self.is_backingup: time.sleep(5) server_users = server_permissions.get_server_user_list(self.server_id) for user in server_users: - websocket_helper.broadcast_user(user,'notification', - "Executable update failed for " + self.name + ". Check log file for details.") + websocket_helper.broadcast_user( + user, + "notification", + "Executable update failed for " + + self.name + + ". Check log file for details.", + ) logger.error("Executable download failed.") - - -#************************************************************************************************ -# Minecraft Servers Statistics -#************************************************************************************************ + # ************************************************************************************************ + # Minecraft Servers Statistics + # ************************************************************************************************ def realtime_stats(self): total_players = 0 @@ -852,61 +1074,65 @@ class Server: raw_ping_result = self.get_raw_server_stats(self.server_id) if f"{raw_ping_result.get('icon')}" == "b''": - raw_ping_result['icon'] = False + raw_ping_result["icon"] = False - servers_ping.append({ - 'id': raw_ping_result.get('id'), - 'started': raw_ping_result.get('started'), - 'running': raw_ping_result.get('running'), - 'cpu': raw_ping_result.get('cpu'), - 'mem': raw_ping_result.get('mem'), - 'mem_percent': raw_ping_result.get('mem_percent'), - 'world_name': raw_ping_result.get('world_name'), - 'world_size': raw_ping_result.get('world_size'), - 'server_port': raw_ping_result.get('server_port'), - 'int_ping_results': raw_ping_result.get('int_ping_results'), - 'online': raw_ping_result.get('online'), - 'max': raw_ping_result.get('max'), - 'players': raw_ping_result.get('players'), - 'desc': raw_ping_result.get('desc'), - 'version': raw_ping_result.get('version'), - 'icon': raw_ping_result.get('icon') - }) + servers_ping.append( + { + "id": raw_ping_result.get("id"), + "started": raw_ping_result.get("started"), + "running": raw_ping_result.get("running"), + "cpu": raw_ping_result.get("cpu"), + "mem": raw_ping_result.get("mem"), + "mem_percent": raw_ping_result.get("mem_percent"), + "world_name": raw_ping_result.get("world_name"), + "world_size": raw_ping_result.get("world_size"), + "server_port": raw_ping_result.get("server_port"), + "int_ping_results": raw_ping_result.get("int_ping_results"), + "online": raw_ping_result.get("online"), + "max": raw_ping_result.get("max"), + "players": raw_ping_result.get("players"), + "desc": raw_ping_result.get("desc"), + "version": raw_ping_result.get("version"), + "icon": raw_ping_result.get("icon"), + } + ) if len(websocket_helper.clients) > 0: websocket_helper.broadcast_page_params( - '/panel/server_detail', + "/panel/server_detail", + {"id": str(self.server_id)}, + "update_server_details", { - 'id': str(self.server_id) + "id": raw_ping_result.get("id"), + "started": raw_ping_result.get("started"), + "running": raw_ping_result.get("running"), + "cpu": raw_ping_result.get("cpu"), + "mem": raw_ping_result.get("mem"), + "mem_percent": raw_ping_result.get("mem_percent"), + "world_name": raw_ping_result.get("world_name"), + "world_size": raw_ping_result.get("world_size"), + "server_port": raw_ping_result.get("server_port"), + "int_ping_results": raw_ping_result.get("int_ping_results"), + "online": raw_ping_result.get("online"), + "max": raw_ping_result.get("max"), + "players": raw_ping_result.get("players"), + "desc": raw_ping_result.get("desc"), + "version": raw_ping_result.get("version"), + "icon": raw_ping_result.get("icon"), }, - 'update_server_details', - { - 'id': raw_ping_result.get('id'), - 'started': raw_ping_result.get('started'), - 'running': raw_ping_result.get('running'), - 'cpu': raw_ping_result.get('cpu'), - 'mem': raw_ping_result.get('mem'), - 'mem_percent': raw_ping_result.get('mem_percent'), - 'world_name': raw_ping_result.get('world_name'), - 'world_size': raw_ping_result.get('world_size'), - 'server_port': raw_ping_result.get('server_port'), - 'int_ping_results': raw_ping_result.get('int_ping_results'), - 'online': raw_ping_result.get('online'), - 'max': raw_ping_result.get('max'), - 'players': raw_ping_result.get('players'), - 'desc': raw_ping_result.get('desc'), - 'version': raw_ping_result.get('version'), - 'icon': raw_ping_result.get('icon') - } ) - total_players += int(raw_ping_result.get('online')) - max_players += int(raw_ping_result.get('max')) + total_players += int(raw_ping_result.get("online")) + max_players += int(raw_ping_result.get("max")) self.record_server_stats() if (len(servers_ping) > 0) & (len(websocket_helper.clients) > 0): try: - websocket_helper.broadcast_page('/panel/dashboard', 'update_server_status', servers_ping) - websocket_helper.broadcast_page('/status', 'update_server_status', servers_ping) + websocket_helper.broadcast_page( + "/panel/dashboard", "update_server_status", servers_ping + ) + websocket_helper.broadcast_page( + "/status", "update_server_status", servers_ping + ) except: console.warning("Can't broadcast server status to websocket") @@ -919,24 +1145,24 @@ class Server: server_id = self.server_id server = servers_helper.get_server_data_by_id(server_id) - logger.debug(f'Getting stats for server: {server_id}') + logger.debug(f"Getting stats for server: {server_id}") # get our server object, settings and data dictionaries self.reload_server_settings() # world data - server_path = server['path'] + server_path = server["path"] # process stats p_stats = Stats._get_process_stats(self.process) # TODO: search server properties file for possible override of 127.0.0.1 - internal_ip = server['server_ip'] - server_port = server['server_port'] - server_name = server.get('server_name', f"ID#{server_id}") + internal_ip = server["server_ip"] + server_port = server["server_port"] + server_name = server.get("server_name", f"ID#{server_id}") logger.debug("Pinging server '{server}' on {internal_ip}:{server_port}") - if servers_helper.get_server_type_by_id(server_id) == 'minecraft-bedrock': + if servers_helper.get_server_type_by_id(server_id) == "minecraft-bedrock": int_mc_ping = ping_bedrock(internal_ip, int(server_port)) else: int_mc_ping = ping(internal_ip, int(server_port)) @@ -947,46 +1173,49 @@ class Server: # if we got a good ping return, let's parse it if int_mc_ping: int_data = True - if servers_helper.get_server_type_by_id(server['server_id']) == 'minecraft-bedrock': + if ( + servers_helper.get_server_type_by_id(server["server_id"]) + == "minecraft-bedrock" + ): ping_data = Stats.parse_server_RakNet_ping(int_mc_ping) else: ping_data = Stats.parse_server_ping(int_mc_ping) - #Makes sure we only show stats when a server is online otherwise people have gotten confused. + # Makes sure we only show stats when a server is online otherwise people have gotten confused. if self.check_running(): server_stats = { - 'id': server_id, - 'started': self.get_start_time(), - 'running': self.check_running(), - 'cpu': p_stats.get('cpu_usage', 0), - 'mem': p_stats.get('memory_usage', 0), - "mem_percent": p_stats.get('mem_percentage', 0), - 'world_name': server_name, - 'world_size': Stats.get_world_size(server_path), - 'server_port': server_port, - 'int_ping_results': int_data, - 'online': ping_data.get("online", False), + "id": server_id, + "started": self.get_start_time(), + "running": self.check_running(), + "cpu": p_stats.get("cpu_usage", 0), + "mem": p_stats.get("memory_usage", 0), + "mem_percent": p_stats.get("mem_percentage", 0), + "world_name": server_name, + "world_size": Stats.get_world_size(server_path), + "server_port": server_port, + "int_ping_results": int_data, + "online": ping_data.get("online", False), "max": ping_data.get("max", False), - 'players': ping_data.get("players", False), - 'desc': ping_data.get("server_description", False), - 'version': ping_data.get("server_version", False) + "players": ping_data.get("players", False), + "desc": ping_data.get("server_description", False), + "version": ping_data.get("server_version", False), } else: server_stats = { - 'id': server_id, - 'started': self.get_start_time(), - 'running': self.check_running(), - 'cpu': p_stats.get('cpu_usage', 0), - 'mem': p_stats.get('memory_usage', 0), - "mem_percent": p_stats.get('mem_percentage', 0), - 'world_name': server_name, - 'world_size': Stats.get_world_size(server_path), - 'server_port': server_port, - 'int_ping_results': int_data, - 'online': False, + "id": server_id, + "started": self.get_start_time(), + "running": self.check_running(), + "cpu": p_stats.get("cpu_usage", 0), + "mem": p_stats.get("memory_usage", 0), + "mem_percent": p_stats.get("mem_percentage", 0), + "world_name": server_name, + "world_size": Stats.get_world_size(server_path), + "server_port": server_port, + "int_ping_results": int_data, + "online": False, "max": False, - 'players': False, - 'desc': False, - 'version': False + "players": False, + "desc": False, + "version": False, } return server_stats @@ -1001,22 +1230,20 @@ class Server: # server_settings = server.get('server_settings', {}) # server_data = server.get('server_data_obj', {}) - # TODO: search server properties file for possible override of 127.0.0.1 - internal_ip = server['server_ip'] - server_port = server['server_port'] + internal_ip = server["server_ip"] + server_port = server["server_port"] logger.debug("Pinging {internal_ip} on port {server_port}") - if servers_helper.get_server_type_by_id(self.server_id) != 'minecraft-bedrock': + if servers_helper.get_server_type_by_id(self.server_id) != "minecraft-bedrock": int_mc_ping = ping(internal_ip, int(server_port)) - ping_data = {} # if we got a good ping return, let's parse it if int_mc_ping: ping_data = Stats.parse_server_ping(int_mc_ping) - return ping_data['players'] + return ping_data["players"] return [] def get_raw_server_stats(self, server_id): @@ -1024,22 +1251,24 @@ class Server: try: server = servers_helper.get_server_obj(server_id) except: - return {'id': server_id, - 'started': False, - 'running': False, - 'cpu': 0, - 'mem': 0, - "mem_percent": 0, - 'world_name': None, - 'world_size': None, - 'server_port': None, - 'int_ping_results': False, - 'online': False, - 'max': False, - 'players': False, - 'desc': False, - 'version': False, - 'icon': False} + return { + "id": server_id, + "started": False, + "running": False, + "cpu": 0, + "mem": 0, + "mem_percent": 0, + "world_name": None, + "world_size": None, + "server_port": None, + "int_ping_results": False, + "online": False, + "max": False, + "players": False, + "desc": False, + "version": False, + "icon": False, + } server_stats = {} server = servers_helper.get_server_obj(server_id) @@ -1047,59 +1276,57 @@ class Server: return {} server_dt = servers_helper.get_server_data_by_id(server_id) - - logger.debug(f'Getting stats for server: {server_id}') + logger.debug(f"Getting stats for server: {server_id}") # get our server object, settings and data dictionaries self.reload_server_settings() # world data - server_name = server_dt['server_name'] - server_path = server_dt['path'] + server_name = server_dt["server_name"] + server_path = server_dt["path"] # process stats p_stats = Stats._get_process_stats(self.process) # TODO: search server properties file for possible override of 127.0.0.1 - #internal_ip = server['server_ip'] - #server_port = server['server_port'] - internal_ip = server_dt['server_ip'] - server_port = server_dt['server_port'] - + # internal_ip = server['server_ip'] + # server_port = server['server_port'] + internal_ip = server_dt["server_ip"] + server_port = server_dt["server_port"] logger.debug(f"Pinging server '{self.name}' on {internal_ip}:{server_port}") - if servers_helper.get_server_type_by_id(server_id) == 'minecraft-bedrock': + if servers_helper.get_server_type_by_id(server_id) == "minecraft-bedrock": int_mc_ping = ping_bedrock(internal_ip, int(server_port)) else: int_mc_ping = ping(internal_ip, int(server_port)) int_data = False ping_data = {} - #Makes sure we only show stats when a server is online otherwise people have gotten confused. + # Makes sure we only show stats when a server is online otherwise people have gotten confused. if self.check_running(): # if we got a good ping return, let's parse it - if servers_helper.get_server_type_by_id(server_id) != 'minecraft-bedrock': + if servers_helper.get_server_type_by_id(server_id) != "minecraft-bedrock": if int_mc_ping: int_data = True ping_data = Stats.parse_server_ping(int_mc_ping) server_stats = { - 'id': server_id, - 'started': self.get_start_time(), - 'running': self.check_running(), - 'cpu': p_stats.get('cpu_usage', 0), - 'mem': p_stats.get('memory_usage', 0), - "mem_percent": p_stats.get('mem_percentage', 0), - 'world_name': server_name, - 'world_size': Stats.get_world_size(server_path), - 'server_port': server_port, - 'int_ping_results': int_data, - 'online': ping_data.get("online", False), + "id": server_id, + "started": self.get_start_time(), + "running": self.check_running(), + "cpu": p_stats.get("cpu_usage", 0), + "mem": p_stats.get("memory_usage", 0), + "mem_percent": p_stats.get("mem_percentage", 0), + "world_name": server_name, + "world_size": Stats.get_world_size(server_path), + "server_port": server_port, + "int_ping_results": int_data, + "online": ping_data.get("online", False), "max": ping_data.get("max", False), - 'players': ping_data.get("players", False), - 'desc': ping_data.get("server_description", False), - 'version': ping_data.get("server_version", False), - 'icon': ping_data.get("server_icon", False) + "players": ping_data.get("players", False), + "desc": ping_data.get("server_description", False), + "version": ping_data.get("server_version", False), + "icon": ping_data.get("server_icon", False), } else: @@ -1107,65 +1334,65 @@ class Server: int_data = True ping_data = Stats.parse_server_RakNet_ping(int_mc_ping) try: - server_icon = base64.encodebytes(ping_data['icon']) - except Exception as ex: + server_icon = base64.encodebytes(ping_data["icon"]) + except Exception as ex: server_icon = False logger.info(f"Unable to read the server icon : {ex}") server_stats = { - 'id': server_id, - 'started': self.get_start_time(), - 'running': self.check_running(), - 'cpu': p_stats.get('cpu_usage', 0), - 'mem': p_stats.get('memory_usage', 0), - "mem_percent": p_stats.get('mem_percentage', 0), - 'world_name': server_name, - 'world_size': Stats.get_world_size(server_path), - 'server_port': server_port, - 'int_ping_results': int_data, - 'online': ping_data['online'], - 'max': ping_data['max'], - 'players': [], - 'desc': ping_data['server_description'], - 'version': ping_data['server_version'], - 'icon': server_icon + "id": server_id, + "started": self.get_start_time(), + "running": self.check_running(), + "cpu": p_stats.get("cpu_usage", 0), + "mem": p_stats.get("memory_usage", 0), + "mem_percent": p_stats.get("mem_percentage", 0), + "world_name": server_name, + "world_size": Stats.get_world_size(server_path), + "server_port": server_port, + "int_ping_results": int_data, + "online": ping_data["online"], + "max": ping_data["max"], + "players": [], + "desc": ping_data["server_description"], + "version": ping_data["server_version"], + "icon": server_icon, } else: server_stats = { - 'id': server_id, - 'started': self.get_start_time(), - 'running': self.check_running(), - 'cpu': p_stats.get('cpu_usage', 0), - 'mem': p_stats.get('memory_usage', 0), - "mem_percent": p_stats.get('mem_percentage', 0), - 'world_name': server_name, - 'world_size': Stats.get_world_size(server_path), - 'server_port': server_port, - 'int_ping_results': int_data, - 'online': False, - 'max': False, - 'players': False, - 'desc': False, - 'version': False, - 'icon': False + "id": server_id, + "started": self.get_start_time(), + "running": self.check_running(), + "cpu": p_stats.get("cpu_usage", 0), + "mem": p_stats.get("memory_usage", 0), + "mem_percent": p_stats.get("mem_percentage", 0), + "world_name": server_name, + "world_size": Stats.get_world_size(server_path), + "server_port": server_port, + "int_ping_results": int_data, + "online": False, + "max": False, + "players": False, + "desc": False, + "version": False, + "icon": False, } else: server_stats = { - 'id': server_id, - 'started': self.get_start_time(), - 'running': self.check_running(), - 'cpu': p_stats.get('cpu_usage', 0), - 'mem': p_stats.get('memory_usage', 0), - "mem_percent": p_stats.get('mem_percentage', 0), - 'world_name': server_name, - 'world_size': Stats.get_world_size(server_path), - 'server_port': server_port, - 'int_ping_results': int_data, - 'online': False, + "id": server_id, + "started": self.get_start_time(), + "running": self.check_running(), + "cpu": p_stats.get("cpu_usage", 0), + "mem": p_stats.get("memory_usage", 0), + "mem_percent": p_stats.get("mem_percentage", 0), + "world_name": server_name, + "world_size": Stats.get_world_size(server_path), + "server_port": server_port, + "int_ping_results": int_data, + "online": False, "max": False, - 'players': False, - 'desc': False, - 'version': False + "players": False, + "desc": False, + "version": False, } return server_stats @@ -1173,23 +1400,25 @@ class Server: def record_server_stats(self): server = self.get_servers_stats() - Server_Stats.insert({ - Server_Stats.server_id: server.get('id', 0), - Server_Stats.started: server.get('started', ""), - Server_Stats.running: server.get('running', False), - Server_Stats.cpu: server.get('cpu', 0), - Server_Stats.mem: server.get('mem', 0), - Server_Stats.mem_percent: server.get('mem_percent', 0), - Server_Stats.world_name: server.get('world_name', ""), - Server_Stats.world_size: server.get('world_size', ""), - Server_Stats.server_port: server.get('server_port', ""), - Server_Stats.int_ping_results: server.get('int_ping_results', False), - Server_Stats.online: server.get("online", False), - Server_Stats.max: server.get("max", False), - Server_Stats.players: server.get("players", False), - Server_Stats.desc: server.get("desc", False), - Server_Stats.version: server.get("version", False) - }).execute() + Server_Stats.insert( + { + Server_Stats.server_id: server.get("id", 0), + Server_Stats.started: server.get("started", ""), + Server_Stats.running: server.get("running", False), + Server_Stats.cpu: server.get("cpu", 0), + Server_Stats.mem: server.get("mem", 0), + Server_Stats.mem_percent: server.get("mem_percent", 0), + Server_Stats.world_name: server.get("world_name", ""), + Server_Stats.world_size: server.get("world_size", ""), + Server_Stats.server_port: server.get("server_port", ""), + Server_Stats.int_ping_results: server.get("int_ping_results", False), + Server_Stats.online: server.get("online", False), + Server_Stats.max: server.get("max", False), + Server_Stats.players: server.get("players", False), + Server_Stats.desc: server.get("desc", False), + Server_Stats.version: server.get("version", False), + } + ).execute() # delete old data max_age = helper.get_setting("history_max_age") diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index bad0c79f..1b9f5db9 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -23,23 +23,24 @@ try: except ModuleNotFoundError as err: helper.auto_installer_fix(err) -logger = logging.getLogger('apscheduler') -scheduler_intervals = { 'seconds', - 'minutes', - 'hours', - 'days', - 'weeks', - 'monday', - 'tuesday', - 'wednesday', - 'thursday', - 'friday', - 'saturday', - 'sunday' - } +logger = logging.getLogger("apscheduler") +scheduler_intervals = { + "seconds", + "minutes", + "hours", + "days", + "weeks", + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + "sunday", +} + class TasksManager: - def __init__(self, controller): self.controller = controller self.tornado = Webserver(controller, self) @@ -49,21 +50,30 @@ class TasksManager: self.users_controller = Users_Controller() - self.webserver_thread = threading.Thread(target=self.tornado.run_tornado, daemon=True, name='tornado_thread') + self.webserver_thread = threading.Thread( + target=self.tornado.run_tornado, daemon=True, name="tornado_thread" + ) self.main_thread_exiting = False - self.schedule_thread = threading.Thread(target=self.scheduler_thread, daemon=True, name="scheduler") + self.schedule_thread = threading.Thread( + target=self.scheduler_thread, daemon=True, name="scheduler" + ) - self.log_watcher_thread = threading.Thread(target=self.log_watcher, daemon=True, name="log_watcher") + self.log_watcher_thread = threading.Thread( + target=self.log_watcher, daemon=True, name="log_watcher" + ) - self.command_thread = threading.Thread(target=self.command_watcher, daemon=True, name="command_watcher") + self.command_thread = threading.Thread( + target=self.command_watcher, daemon=True, name="command_watcher" + ) - self.realtime_thread = threading.Thread(target=self.realtime, daemon=True, name="realtime") + self.realtime_thread = threading.Thread( + target=self.realtime, daemon=True, name="realtime" + ) self.reload_schedule_from_db() - def get_main_thread_run_status(self): return self.main_thread_exiting @@ -81,16 +91,18 @@ class TasksManager: try: svr = self.controller.get_server_obj(c.server_id) except: - logger.error("Server value requested does note exist purging item from waiting commands.") + logger.error( + "Server value requested does note exist purging item from waiting commands." + ) management_helper.mark_command_complete(c.command_id) user_id = c.user_id command = c.command - if command == 'start_server': + if command == "start_server": svr.run_threaded_server(user_id) - elif command == 'stop_server': + elif command == "stop_server": svr.stop_threaded_server() elif command == "restart_server": @@ -125,7 +137,9 @@ class TasksManager: self.tornado.stop_web_server() console.info("Waiting 3 seconds") time.sleep(3) - self.webserver_thread = threading.Thread(target=self.tornado.run_tornado, daemon=True, name='tornado_thread') + self.webserver_thread = threading.Thread( + target=self.tornado.run_tornado, daemon=True, name="tornado_thread" + ) self.start_webserver() def stop_webserver(self): @@ -148,64 +162,77 @@ class TasksManager: def scheduler_thread(self): schedules = management_helper.get_schedules_enabled() self.scheduler.add_listener(self.schedule_watcher, mask=EVENT_JOB_EXECUTED) - #self.scheduler.add_job(self.scheduler.print_jobs, 'interval', seconds=10, id='-1') + # self.scheduler.add_job(self.scheduler.print_jobs, 'interval', seconds=10, id='-1') - #load schedules from DB + # load schedules from DB for schedule in schedules: - if schedule.interval != 'reaction': + if schedule.interval != "reaction": if schedule.cron_string != "": try: - self.scheduler.add_job(management_helper.add_command, - CronTrigger.from_crontab(schedule.cron_string, - timezone=str(self.tz)), - id = str(schedule.schedule_id), - args = [schedule.server_id, - self.users_controller.get_id_by_name('system'), - '127.0.0.1', - schedule.command] - ) + self.scheduler.add_job( + management_helper.add_command, + CronTrigger.from_crontab( + schedule.cron_string, timezone=str(self.tz) + ), + id=str(schedule.schedule_id), + args=[ + schedule.server_id, + self.users_controller.get_id_by_name("system"), + "127.0.0.1", + schedule.command, + ], + ) except Exception as e: console.error(f"Failed to schedule task with error: {e}.") console.warning("Removing failed task from DB.") logger.error(f"Failed to schedule task with error: {e}.") logger.warning("Removing failed task from DB.") - #remove items from DB if task fails to add to apscheduler + # remove items from DB if task fails to add to apscheduler management_helper.delete_scheduled_task(schedule.schedule_id) else: - if schedule.interval_type == 'hours': - self.scheduler.add_job(management_helper.add_command, - 'cron', - minute = 0, - hour = '*/'+str(schedule.interval), - id = str(schedule.schedule_id), - args = [schedule.server_id, - self.users_controller.get_id_by_name('system'), - '127.0.0.1', - schedule.command] - ) - elif schedule.interval_type == 'minutes': - self.scheduler.add_job(management_helper.add_command, - 'cron', - minute = '*/'+str(schedule.interval), - id = str(schedule.schedule_id), - args = [schedule.server_id, - self.users_controller.get_id_by_name('system'), - '127.0.0.1', - schedule.command] - ) - elif schedule.interval_type == 'days': - curr_time = schedule.start_time.split(':') - self.scheduler.add_job(management_helper.add_command, - 'cron', - day = '*/'+str(schedule.interval), - hour=curr_time[0], - minute=curr_time[1], - id=str(schedule.schedule_id), - args=[schedule.server_id, - self.users_controller.get_id_by_name('system'), - '127.0.0.1', - schedule.command] - ) + if schedule.interval_type == "hours": + self.scheduler.add_job( + management_helper.add_command, + "cron", + minute=0, + hour="*/" + str(schedule.interval), + id=str(schedule.schedule_id), + args=[ + schedule.server_id, + self.users_controller.get_id_by_name("system"), + "127.0.0.1", + schedule.command, + ], + ) + elif schedule.interval_type == "minutes": + self.scheduler.add_job( + management_helper.add_command, + "cron", + minute="*/" + str(schedule.interval), + id=str(schedule.schedule_id), + args=[ + schedule.server_id, + self.users_controller.get_id_by_name("system"), + "127.0.0.1", + schedule.command, + ], + ) + elif schedule.interval_type == "days": + curr_time = schedule.start_time.split(":") + self.scheduler.add_job( + management_helper.add_command, + "cron", + day="*/" + str(schedule.interval), + hour=curr_time[0], + minute=curr_time[1], + id=str(schedule.schedule_id), + args=[ + schedule.server_id, + self.users_controller.get_id_by_name("system"), + "127.0.0.1", + schedule.command, + ], + ) self.scheduler.start() jobs = self.scheduler.get_jobs() logger.info("Loaded schedules. Current enabled schedules: ") @@ -214,76 +241,90 @@ class TasksManager: def schedule_job(self, job_data): sch_id = management_helper.create_scheduled_task( - job_data['server_id'], - job_data['action'], - job_data['interval'], - job_data['interval_type'], - job_data['start_time'], - job_data['command'], + job_data["server_id"], + job_data["action"], + job_data["interval"], + job_data["interval_type"], + job_data["start_time"], + job_data["command"], "None", - job_data['enabled'], - job_data['one_time'], - job_data['cron_string'], - job_data['parent'], - job_data['delay']) - #Checks to make sure some doofus didn't actually make the newly created task a child of itself. - if str(job_data['parent']) == str(sch_id): - management_helper.update_scheduled_task(sch_id, {'parent':None}) - #Check to see if it's enabled and is not a chain reaction. - if job_data['enabled'] and job_data['interval_type'] != 'reaction': - if job_data['cron_string'] != "": + job_data["enabled"], + job_data["one_time"], + job_data["cron_string"], + job_data["parent"], + job_data["delay"], + ) + # Checks to make sure some doofus didn't actually make the newly created task a child of itself. + if str(job_data["parent"]) == str(sch_id): + management_helper.update_scheduled_task(sch_id, {"parent": None}) + # Check to see if it's enabled and is not a chain reaction. + if job_data["enabled"] and job_data["interval_type"] != "reaction": + if job_data["cron_string"] != "": try: - self.scheduler.add_job(management_helper.add_command, - CronTrigger.from_crontab(job_data['cron_string'], - timezone=str(self.tz)), - id=str(sch_id), - args=[job_data['server_id'], - self.users_controller.get_id_by_name('system'), - '127.0.0.1', - job_data['command']] - ) + self.scheduler.add_job( + management_helper.add_command, + CronTrigger.from_crontab( + job_data["cron_string"], timezone=str(self.tz) + ), + id=str(sch_id), + args=[ + job_data["server_id"], + self.users_controller.get_id_by_name("system"), + "127.0.0.1", + job_data["command"], + ], + ) except Exception as e: console.error(f"Failed to schedule task with error: {e}.") console.warning("Removing failed task from DB.") logger.error(f"Failed to schedule task with error: {e}.") logger.warning("Removing failed task from DB.") - #remove items from DB if task fails to add to apscheduler + # remove items from DB if task fails to add to apscheduler management_helper.delete_scheduled_task(sch_id) else: - if job_data['interval_type'] == 'hours': - self.scheduler.add_job(management_helper.add_command, - 'cron', - minute = 0, - hour = '*/'+str(job_data['interval']), - id=str(sch_id), - args=[job_data['server_id'], - self.users_controller.get_id_by_name('system'), - '127.0.0.1', - job_data['command']] - ) - elif job_data['interval_type'] == 'minutes': - self.scheduler.add_job(management_helper.add_command, - 'cron', - minute = '*/'+str(job_data['interval']), - id=str(sch_id), - args=[job_data['server_id'], - self.users_controller.get_id_by_name('system'), - '127.0.0.1', - job_data['command']] - ) - elif job_data['interval_type'] == 'days': - curr_time = job_data['start_time'].split(':') - self.scheduler.add_job(management_helper.add_command, - 'cron', - day = '*/'+str(job_data['interval']), - hour = curr_time[0], - minute = curr_time[1], - id=str(sch_id), - args=[job_data['server_id'], - self.users_controller.get_id_by_name('system'), - '127.0.0.1', - job_data['command']], - ) + if job_data["interval_type"] == "hours": + self.scheduler.add_job( + management_helper.add_command, + "cron", + minute=0, + hour="*/" + str(job_data["interval"]), + id=str(sch_id), + args=[ + job_data["server_id"], + self.users_controller.get_id_by_name("system"), + "127.0.0.1", + job_data["command"], + ], + ) + elif job_data["interval_type"] == "minutes": + self.scheduler.add_job( + management_helper.add_command, + "cron", + minute="*/" + str(job_data["interval"]), + id=str(sch_id), + args=[ + job_data["server_id"], + self.users_controller.get_id_by_name("system"), + "127.0.0.1", + job_data["command"], + ], + ) + elif job_data["interval_type"] == "days": + curr_time = job_data["start_time"].split(":") + self.scheduler.add_job( + management_helper.add_command, + "cron", + day="*/" + str(job_data["interval"]), + hour=curr_time[0], + minute=curr_time[1], + id=str(sch_id), + args=[ + job_data["server_id"], + self.users_controller.get_id_by_name("system"), + "127.0.0.1", + job_data["command"], + ], + ) logger.info("Added job. Current enabled schedules: ") jobs = self.scheduler.get_jobs() for item in jobs: @@ -292,134 +333,183 @@ class TasksManager: def remove_all_server_tasks(self, server_id): schedules = management_helper.get_schedules_by_server(server_id) for schedule in schedules: - if schedule.interval != 'reaction': + if schedule.interval != "reaction": self.remove_job(schedule.schedule_id) def remove_job(self, sch_id): job = management_helper.get_scheduled_task_model(sch_id) for schedule in management_helper.get_child_schedules(sch_id): - management_helper.update_scheduled_task(schedule.schedule_id, {'parent':None}) + management_helper.update_scheduled_task( + schedule.schedule_id, {"parent": None} + ) management_helper.delete_scheduled_task(sch_id) - if job.enabled and job.interval_type != 'reaction': + if job.enabled and job.interval_type != "reaction": self.scheduler.remove_job(str(sch_id)) logger.info(f"Job with ID {sch_id} was deleted.") else: - logger.info(f"Job with ID {sch_id} was deleted from DB, but was not enabled." - + "Not going to try removing something that doesn't exist from active schedules.") + logger.info( + f"Job with ID {sch_id} was deleted from DB, but was not enabled." + + "Not going to try removing something that doesn't exist from active schedules." + ) def update_job(self, sch_id, job_data): management_helper.update_scheduled_task(sch_id, job_data) - #Checks to make sure some doofus didn't actually make the newly created task a child of itself. - if str(job_data['parent']) == str(sch_id): - management_helper.update_scheduled_task(sch_id, {'parent':None}) + # Checks to make sure some doofus didn't actually make the newly created task a child of itself. + if str(job_data["parent"]) == str(sch_id): + management_helper.update_scheduled_task(sch_id, {"parent": None}) try: - if job_data['interval'] != 'reaction': + if job_data["interval"] != "reaction": self.scheduler.remove_job(str(sch_id)) except: - logger.info("No job found in update job. Assuming it was previously disabled. Starting new job.") + logger.info( + "No job found in update job. Assuming it was previously disabled. Starting new job." + ) - if job_data['enabled']: - if job_data['interval'] != 'reaction': - if job_data['cron_string'] != "": + if job_data["enabled"]: + if job_data["interval"] != "reaction": + if job_data["cron_string"] != "": try: - self.scheduler.add_job(management_helper.add_command, - CronTrigger.from_crontab(job_data['cron_string'], - timezone=str(self.tz)), - id=str(sch_id), - args=[job_data['server_id'], - self.users_controller.get_id_by_name('system'), - '127.0.0.1', - job_data['command']] - ) + self.scheduler.add_job( + management_helper.add_command, + CronTrigger.from_crontab( + job_data["cron_string"], timezone=str(self.tz) + ), + id=str(sch_id), + args=[ + job_data["server_id"], + self.users_controller.get_id_by_name("system"), + "127.0.0.1", + job_data["command"], + ], + ) except Exception as e: console.error(f"Failed to schedule task with error: {e}.") console.info("Removing failed task from DB.") management_helper.delete_scheduled_task(sch_id) else: - if job_data['interval_type'] == 'hours': - self.scheduler.add_job(management_helper.add_command, - 'cron', - minute = 0, - hour = '*/'+str(job_data['interval']), - id=str(sch_id), - args=[job_data['server_id'], - self.users_controller.get_id_by_name('system'), - '127.0.0.1', - job_data['command']] - ) - elif job_data['interval_type'] == 'minutes': - self.scheduler.add_job(management_helper.add_command, - 'cron', - minute = '*/'+str(job_data['interval']), - id=str(sch_id), - args=[job_data['server_id'], - self.users_controller.get_id_by_name('system'), - '127.0.0.1', - job_data['command']] - ) - elif job_data['interval_type'] == 'days': - curr_time = job_data['start_time'].split(':') - self.scheduler.add_job(management_helper.add_command, - 'cron', - day = '*/'+str(job_data['interval']), - hour = curr_time[0], - minute = curr_time[1], - id=str(sch_id), - args=[job_data['server_id'], - self.users_controller.get_id_by_name('system'), - '127.0.0.1', - job_data['command']] - ) + if job_data["interval_type"] == "hours": + self.scheduler.add_job( + management_helper.add_command, + "cron", + minute=0, + hour="*/" + str(job_data["interval"]), + id=str(sch_id), + args=[ + job_data["server_id"], + self.users_controller.get_id_by_name("system"), + "127.0.0.1", + job_data["command"], + ], + ) + elif job_data["interval_type"] == "minutes": + self.scheduler.add_job( + management_helper.add_command, + "cron", + minute="*/" + str(job_data["interval"]), + id=str(sch_id), + args=[ + job_data["server_id"], + self.users_controller.get_id_by_name("system"), + "127.0.0.1", + job_data["command"], + ], + ) + elif job_data["interval_type"] == "days": + curr_time = job_data["start_time"].split(":") + self.scheduler.add_job( + management_helper.add_command, + "cron", + day="*/" + str(job_data["interval"]), + hour=curr_time[0], + minute=curr_time[1], + id=str(sch_id), + args=[ + job_data["server_id"], + self.users_controller.get_id_by_name("system"), + "127.0.0.1", + job_data["command"], + ], + ) else: try: self.scheduler.get_job(str(sch_id)) self.scheduler.remove_job(str(sch_id)) except: - logger.info(f"APScheduler found no scheduled job on schedule update for schedule with id: {sch_id} Assuming it was already disabled.") + logger.info( + f"APScheduler found no scheduled job on schedule update for schedule with id: {sch_id} Assuming it was already disabled." + ) def schedule_watcher(self, event): if not event.exception: if str(event.job_id).isnumeric(): task = management_helper.get_scheduled_task_model(int(event.job_id)) - management_helper.add_to_audit_log_raw('system', users_helper.get_user_id_by_name('system'), task.server_id, - f"Task with id {task.schedule_id} completed successfully", '127.0.0.1') - #check if the task is a single run. + management_helper.add_to_audit_log_raw( + "system", + users_helper.get_user_id_by_name("system"), + task.server_id, + f"Task with id {task.schedule_id} completed successfully", + "127.0.0.1", + ) + # check if the task is a single run. if task.one_time: self.remove_job(task.schedule_id) logger.info("one time task detected. Deleting...") -#check for any child tasks for this. It's kind of backward, but this makes DB management a lot easier. One to one instead of one to many. - for schedule in management_helper.get_child_schedules_by_server(task.schedule_id, task.server_id): - #event job ID's are strings so we need to look at this as the same data type. + # check for any child tasks for this. It's kind of backward, but this makes DB management a lot easier. One to one instead of one to many. + for schedule in management_helper.get_child_schedules_by_server( + task.schedule_id, task.server_id + ): + # event job ID's are strings so we need to look at this as the same data type. if str(schedule.parent) == str(event.job_id): if schedule.enabled: - delaytime = datetime.datetime.now() + datetime.timedelta(seconds=schedule.delay) - self.scheduler.add_job(management_helper.add_command, 'date', run_date=delaytime, id=str(schedule.schedule_id), - args=[schedule.server_id, - self.users_controller.get_id_by_name('system'), - '127.0.0.1', - schedule.command]) + delaytime = datetime.datetime.now() + datetime.timedelta( + seconds=schedule.delay + ) + self.scheduler.add_job( + management_helper.add_command, + "date", + run_date=delaytime, + id=str(schedule.schedule_id), + args=[ + schedule.server_id, + self.users_controller.get_id_by_name("system"), + "127.0.0.1", + schedule.command, + ], + ) else: - logger.info("Event job ID is not numerical. Assuming it's stats - not stored in DB. Moving on.") + logger.info( + "Event job ID is not numerical. Assuming it's stats - not stored in DB. Moving on." + ) else: logger.error(f"Task failed with error: {event.exception}") def start_stats_recording(self): - stats_update_frequency = helper.get_setting('stats_update_frequency') - logger.info(f"Stats collection frequency set to {stats_update_frequency} seconds") - console.info(f"Stats collection frequency set to {stats_update_frequency} seconds") + stats_update_frequency = helper.get_setting("stats_update_frequency") + logger.info( + f"Stats collection frequency set to {stats_update_frequency} seconds" + ) + console.info( + f"Stats collection frequency set to {stats_update_frequency} seconds" + ) # one for now, self.controller.stats.record_stats() # one for later - self.scheduler.add_job(self.controller.stats.record_stats, 'interval', seconds=stats_update_frequency, id="stats") - + self.scheduler.add_job( + self.controller.stats.record_stats, + "interval", + seconds=stats_update_frequency, + id="stats", + ) def serverjar_cache_refresher(self): logger.info("Refreshing serverjars.com cache on start") server_jar_obj.refresh_cache() logger.info("Scheduling Serverjars.com cache refresh service every 12 hours") - self.scheduler.add_job(server_jar_obj.refresh_cache, 'interval', hours=12, id="serverjars") + self.scheduler.add_job( + server_jar_obj.refresh_cache, "interval", hours=12, id="serverjars" + ) def realtime(self): loop = asyncio.new_event_loop() @@ -429,24 +519,38 @@ class TasksManager: while True: - if host_stats.get('cpu_usage') != \ - management_helper.get_latest_hosts_stats().get('cpu_usage') or \ - host_stats.get('mem_percent') != \ - management_helper.get_latest_hosts_stats().get('mem_percent'): + if host_stats.get( + "cpu_usage" + ) != management_helper.get_latest_hosts_stats().get( + "cpu_usage" + ) or host_stats.get( + "mem_percent" + ) != management_helper.get_latest_hosts_stats().get( + "mem_percent" + ): # Stats are different host_stats = management_helper.get_latest_hosts_stats() if len(websocket_helper.clients) > 0: # There are clients - websocket_helper.broadcast_page('/panel/dashboard', 'update_host_stats', { - 'cpu_usage': host_stats.get('cpu_usage'), - 'cpu_cores': host_stats.get('cpu_cores'), - 'cpu_cur_freq': host_stats.get('cpu_cur_freq'), - 'cpu_max_freq': host_stats.get('cpu_max_freq'), - 'mem_percent': host_stats.get('mem_percent'), - 'mem_usage': host_stats.get('mem_usage') - }) + websocket_helper.broadcast_page( + "/panel/dashboard", + "update_host_stats", + { + "cpu_usage": host_stats.get("cpu_usage"), + "cpu_cores": host_stats.get("cpu_cores"), + "cpu_cur_freq": host_stats.get("cpu_cur_freq"), + "cpu_max_freq": host_stats.get("cpu_max_freq"), + "mem_percent": host_stats.get("mem_percent"), + "mem_usage": host_stats.get("mem_usage"), + }, + ) def log_watcher(self): self.controller.servers.check_for_old_logs() - self.scheduler.add_job(self.controller.servers.check_for_old_logs, 'interval', hours=6, id="log-mgmt") + self.scheduler.add_job( + self.controller.servers.check_for_old_logs, + "interval", + hours=6, + id="log-mgmt", + ) diff --git a/app/classes/shared/translation.py b/app/classes/shared/translation.py index 63730647..d2e98573 100644 --- a/app/classes/shared/translation.py +++ b/app/classes/shared/translation.py @@ -8,17 +8,18 @@ from app.classes.shared.helpers import helper logger = logging.getLogger(__name__) + class Translation: def __init__(self): - self.translations_path = os.path.join(helper.root_dir, 'app', 'translations') + self.translations_path = os.path.join(helper.root_dir, "app", "translations") self.cached_translation = None self.cached_translation_lang = None def get_language_file(self, language: str): - return os.path.join(self.translations_path, str(language) + '.json') + return os.path.join(self.translations_path, str(language) + ".json") def translate(self, page, word, language): - fallback_language = 'en_EN' + fallback_language = "en_EN" translated_word = self.translate_inner(page, word, language) if translated_word is None: @@ -31,20 +32,20 @@ class Translation: elif isinstance(translated_word, str): # Basic strings return translated_word - elif hasattr(translated_word, '__iter__'): + elif hasattr(translated_word, "__iter__"): # Multiline strings - return '\n'.join(translated_word) - return 'Error while getting translation' + return "\n".join(translated_word) + return "Error while getting translation" def translate_inner(self, page, word, language) -> t.Union[t.Any, None]: language_file = self.get_language_file(language) try: if not self.cached_translation: - with open(language_file, 'r', encoding='utf-8') as f: + with open(language_file, "r", encoding="utf-8") as f: data = json.load(f) self.cached_translation = data elif self.cached_translation_lang != language: - with open(language_file, 'r', encoding='utf-8') as f: + with open(language_file, "r", encoding="utf-8") as f: data = json.load(f) self.cached_translation = data self.cached_translation_lang = language @@ -54,21 +55,33 @@ class Translation: try: translated_page = data[page] except KeyError: - logger.error(f'Translation File Error: page {page} does not exist for lang {language}') - console.error(f'Translation File Error: page {page} does not exist for lang {language}') + logger.error( + f"Translation File Error: page {page} does not exist for lang {language}" + ) + console.error( + f"Translation File Error: page {page} does not exist for lang {language}" + ) return None try: translated_word = translated_page[word] return translated_word except KeyError: - logger.error(f'Translation File Error: word {word} does not exist on page {page} for lang {language}') - console.error(f'Translation File Error: word {word} does not exist on page {page} for lang {language}') + logger.error( + f"Translation File Error: word {word} does not exist on page {page} for lang {language}" + ) + console.error( + f"Translation File Error: word {word} does not exist on page {page} for lang {language}" + ) return None except Exception as e: - logger.critical(f'Translation File Error: Unable to read {language_file} due to {e}') - console.critical(f'Translation File Error: Unable to read {language_file} due to {e}') + logger.critical( + f"Translation File Error: Unable to read {language_file} due to {e}" + ) + console.critical( + f"Translation File Error: Unable to read {language_file} due to {e}" + ) return None diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py index dc916ed1..77b79828 100644 --- a/app/classes/web/ajax_handler.py +++ b/app/classes/web/ajax_handler.py @@ -22,8 +22,8 @@ except ModuleNotFoundError as ex: logger = logging.getLogger(__name__) -class AjaxHandler(BaseHandler): +class AjaxHandler(BaseHandler): def render_page(self, template, page_data): self.render( template, @@ -34,22 +34,19 @@ class AjaxHandler(BaseHandler): @tornado.web.authenticated def get(self, page): _, _, exec_user = self.current_user - error = bleach.clean(self.get_argument('error', "WTF Error!")) + error = bleach.clean(self.get_argument("error", "WTF Error!")) template = "panel/denied.html" - page_data = { - 'user_data': exec_user, - 'error': error - } + page_data = {"user_data": exec_user, "error": error} if page == "error": template = "public/error.html" self.render_page(template, page_data) - elif page == 'server_log': - server_id = self.get_argument('id', None) - full_log = self.get_argument('full', False) + elif page == "server_log": + server_id = self.get_argument("id", None) + full_log = self.get_argument("full", False) if server_id is None: logger.warning("Server ID not found in server_log ajax call") @@ -64,22 +61,26 @@ class AjaxHandler(BaseHandler): self.redirect("/panel/error?error=Server ID Not Found") return - if not server_data['log_path']: - logger.warning(f"Log path not found in server_log ajax call ({server_id})") + if not server_data["log_path"]: + logger.warning( + f"Log path not found in server_log ajax call ({server_id})" + ) if full_log: - log_lines = helper.get_setting('max_log_lines') - data = helper.tail_file(helper.get_os_understandable_path(server_data['log_path']), log_lines) + log_lines = helper.get_setting("max_log_lines") + data = helper.tail_file( + helper.get_os_understandable_path(server_data["log_path"]), + log_lines, + ) else: data = ServerOutBuf.lines.get(server_id, []) - for d in data: try: - d = re.sub('(\033\\[(0;)?[0-9]*[A-z]?(;[0-9])?m?)|(> )', '', d) - d = re.sub('[A-z]{2}\b\b', '', d) + d = re.sub("(\033\\[(0;)?[0-9]*[A-z]?(;[0-9])?m?)|(> )", "", d) + d = re.sub("[A-z]{2}\b\b", "", d) line = helper.log_colors(html.escape(d)) - self.write(f'{line}
            ') + self.write(f"{line}
            ") # self.write(d.encode("utf-8")) except Exception as e: @@ -87,27 +88,32 @@ class AjaxHandler(BaseHandler): elif page == "announcements": data = helper.get_announcements() - page_data['notify_data'] = data - self.render_page('ajax/notify.html', page_data) - + page_data["notify_data"] = data + self.render_page("ajax/notify.html", page_data) elif page == "get_zip_tree": - path = self.get_argument('path', None) + path = self.get_argument("path", None) - self.write(helper.get_os_understandable_path(path) + '\n' + - helper.generate_zip_tree(path)) + self.write( + helper.get_os_understandable_path(path) + + "\n" + + helper.generate_zip_tree(path) + ) self.finish() elif page == "get_zip_dir": - path = self.get_argument('path', None) + path = self.get_argument("path", None) - self.write(helper.get_os_understandable_path(path) + '\n' + - helper.generate_zip_dir(path)) + self.write( + helper.get_os_understandable_path(path) + + "\n" + + helper.generate_zip_dir(path) + ) self.finish() elif page == "get_backup_tree": - server_id = self.get_argument('id', None) - folder = self.get_argument('path', None) + server_id = self.get_argument("id", None) + folder = self.get_argument("path", None) output = "" @@ -119,18 +125,19 @@ class AjaxHandler(BaseHandler): dir_list.append(item) else: unsorted_files.append(item) - file_list = sorted(dir_list, key=str.casefold) + sorted(unsorted_files, key=str.casefold) - output += \ - f"""
              """\ - + file_list = sorted(dir_list, key=str.casefold) + sorted( + unsorted_files, key=str.casefold + ) + output += f"""
                """ for raw_filename in file_list: filename = html.escape(raw_filename) rel = os.path.join(folder, raw_filename) dpath = os.path.join(folder, filename) - if str(dpath) in self.controller.management.get_excluded_backup_dirs(server_id): + if str(dpath) in self.controller.management.get_excluded_backup_dirs( + server_id + ): if os.path.isdir(rel): - output += \ - f"""
              • + output += f"""
              • \n
                @@ -139,8 +146,7 @@ class AjaxHandler(BaseHandler): {filename}
              • - \n"""\ - + \n""" else: output += f"""
              • + output += f"""
              • \n
                @@ -161,8 +166,7 @@ class AjaxHandler(BaseHandler): {filename}
              • - \n"""\ - + \n""" else: output += f"""
              • {filename}
              • """ - self.write(helper.get_os_understandable_path(folder) + '\n' + - output) + self.write(helper.get_os_understandable_path(folder) + "\n" + output) self.finish() elif page == "get_backup_dir": - server_id = self.get_argument('id', None) - folder = self.get_argument('path', None) + server_id = self.get_argument("id", None) + folder = self.get_argument("path", None) output = "" dir_list = [] @@ -187,18 +190,19 @@ class AjaxHandler(BaseHandler): dir_list.append(item) else: unsorted_files.append(item) - file_list = sorted(dir_list, key=str.casefold) + sorted(unsorted_files, key=str.casefold) - output += \ - f"""
                  """\ - + file_list = sorted(dir_list, key=str.casefold) + sorted( + unsorted_files, key=str.casefold + ) + output += f"""
                    """ for raw_filename in file_list: filename = html.escape(raw_filename) rel = os.path.join(folder, raw_filename) dpath = os.path.join(folder, filename) - if str(dpath) in self.controller.management.get_excluded_backup_dirs(server_id): + if str(dpath) in self.controller.management.get_excluded_backup_dirs( + server_id + ): if os.path.isdir(rel): - output += \ - f"""
                  • + output += f"""
                  • \n
                    @@ -206,8 +210,7 @@ class AjaxHandler(BaseHandler): {filename} -
                  • """\ - +
                  • """ else: output += f"""
                  • + output += f"""
                  • \n
                    @@ -227,8 +229,7 @@ class AjaxHandler(BaseHandler): {filename} -
                  • """\ - +
                  • """ else: output += f"""
                  • {filename}
                  • """ - self.write(helper.get_os_understandable_path(folder) + '\n' + - output) + self.write(helper.get_os_understandable_path(folder) + "\n" + output) self.finish() elif page == "get_dir": - server_id = self.get_argument('id', None) - path = self.get_argument('path', None) + server_id = self.get_argument("id", None) + path = self.get_argument("path", None) - if not self.check_server_id(server_id, 'get_tree'): + if not self.check_server_id(server_id, "get_tree"): return else: server_id = bleach.clean(server_id) - if helper.validate_traversal(self.controller.servers.get_server_data_by_id(server_id)['path'], path): - self.write(helper.get_os_understandable_path(path) + '\n' + - helper.generate_dir(path)) + if helper.validate_traversal( + self.controller.servers.get_server_data_by_id(server_id)["path"], path + ): + self.write( + helper.get_os_understandable_path(path) + + "\n" + + helper.generate_dir(path) + ) self.finish() @tornado.web.authenticated def post(self, page): api_key, _, exec_user = self.current_user - superuser = exec_user['superuser'] + superuser = exec_user["superuser"] if api_key is not None: superuser = superuser and api_key.superuser - server_id = self.get_argument('id', None) + server_id = self.get_argument("id", None) permissions = { - 'Commands': Enum_Permissions_Server.Commands, - 'Terminal': Enum_Permissions_Server.Terminal, - 'Logs': Enum_Permissions_Server.Logs, - 'Schedule': Enum_Permissions_Server.Schedule, - 'Backup': Enum_Permissions_Server.Backup, - 'Files': Enum_Permissions_Server.Files, - 'Config': Enum_Permissions_Server.Config, - 'Players': Enum_Permissions_Server.Players, - } - user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id) + "Commands": Enum_Permissions_Server.Commands, + "Terminal": Enum_Permissions_Server.Terminal, + "Logs": Enum_Permissions_Server.Logs, + "Schedule": Enum_Permissions_Server.Schedule, + "Backup": Enum_Permissions_Server.Backup, + "Files": Enum_Permissions_Server.Files, + "Config": Enum_Permissions_Server.Config, + "Players": Enum_Permissions_Server.Players, + } + user_perms = self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) if page == "send_command": - command = self.get_body_argument('command', default=None, strip=True) - server_id = self.get_argument('id', None) + command = self.get_body_argument("command", default=None, strip=True) + server_id = self.get_argument("id", None) if server_id is None: logger.warning("Server ID not found in send_command ajax call") @@ -286,54 +293,72 @@ class AjaxHandler(BaseHandler): srv_obj = self.controller.get_server_obj(server_id) - if command == srv_obj.settings['stop_command']: - logger.info("Stop command detected as terminal input - intercepting." + - f"Starting Crafty's stop process for server with id: {server_id}") - self.controller.management.send_command(exec_user['user_id'], server_id, self.get_remote_ip(), 'stop_server') + if command == srv_obj.settings["stop_command"]: + logger.info( + "Stop command detected as terminal input - intercepting." + + f"Starting Crafty's stop process for server with id: {server_id}" + ) + self.controller.management.send_command( + exec_user["user_id"], server_id, self.get_remote_ip(), "stop_server" + ) command = None - elif command == 'restart': - logger.info("Restart command detected as terminal input - intercepting." + - f"Starting Crafty's stop process for server with id: {server_id}") - self.controller.management.send_command(exec_user['user_id'], server_id, self.get_remote_ip(), 'restart_server') + elif command == "restart": + logger.info( + "Restart command detected as terminal input - intercepting." + + f"Starting Crafty's stop process for server with id: {server_id}" + ) + self.controller.management.send_command( + exec_user["user_id"], + server_id, + self.get_remote_ip(), + "restart_server", + ) command = None if command: if srv_obj.check_running(): srv_obj.send_command(command) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Sent command to {self.controller.servers.get_server_friendly_name(server_id)} terminal: {command}", - server_id, - self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Sent command to {self.controller.servers.get_server_friendly_name(server_id)} terminal: {command}", + server_id, + self.get_remote_ip(), + ) elif page == "send_order": - self.controller.users.update_server_order(exec_user['user_id'], bleach.clean(self.get_argument('order'))) + self.controller.users.update_server_order( + exec_user["user_id"], bleach.clean(self.get_argument("order")) + ) return elif page == "backup_now": - server_id = self.get_argument('id', None) + server_id = self.get_argument("id", None) if server_id is None: logger.error("Server ID is none. Canceling backup!") return server = self.controller.get_server_obj(server_id) self.controller.management.add_to_audit_log_raw( - self.controller.users.get_user_by_id(exec_user['user_id'])['username'], exec_user['user_id'], server_id, + self.controller.users.get_user_by_id(exec_user["user_id"])["username"], + exec_user["user_id"], + server_id, f"Backup now executed for server {server_id} ", - source_ip=self.get_remote_ip()) + source_ip=self.get_remote_ip(), + ) server.backup_server() elif page == "clear_comms": - if exec_user['superuser']: + if exec_user["superuser"]: self.controller.clear_unexecuted_commands() return elif page == "kill": - if not permissions['Commands'] in user_perms: + if not permissions["Commands"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Commands") return - server_id = self.get_argument('id', None) + server_id = self.get_argument("id", None) svr = self.controller.get_server_obj(server_id) try: svr.kill() @@ -341,175 +366,214 @@ class AjaxHandler(BaseHandler): svr.cleanup_server_object() svr.record_server_stats() except Exception as e: - logger.error(f"Could not find PID for requested termsig. Full error: {e}") + logger.error( + f"Could not find PID for requested termsig. Full error: {e}" + ) return elif page == "eula": - server_id = self.get_argument('id', None) + server_id = self.get_argument("id", None) svr = self.controller.get_server_obj(server_id) - svr.agree_eula(exec_user['user_id']) + svr.agree_eula(exec_user["user_id"]) elif page == "restore_backup": - if not permissions['Backup'] in user_perms: + if not permissions["Backup"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Backups") return - server_id = bleach.clean(self.get_argument('id', None)) - zip_name = bleach.clean(self.get_argument('zip_file', None)) + server_id = bleach.clean(self.get_argument("id", None)) + zip_name = bleach.clean(self.get_argument("zip_file", None)) svr_obj = self.controller.servers.get_server_obj(server_id) server_data = self.controller.servers.get_server_data_by_id(server_id) - if server_data['type'] == 'minecraft-java': + if server_data["type"] == "minecraft-java": backup_path = svr_obj.backup_path if helper.validate_traversal(backup_path, zip_name): tempDir = helper.unzip_backup_archive(backup_path, zip_name) - new_server = self.controller.import_zip_server(svr_obj.server_name, - tempDir, - server_data['executable'], - '1', '2', - server_data['server_port']) + new_server = self.controller.import_zip_server( + svr_obj.server_name, + tempDir, + server_data["executable"], + "1", + "2", + server_data["server_port"], + ) new_server_id = new_server new_server = self.controller.get_server_data(new_server) - self.controller.rename_backup_dir(server_id, new_server_id, new_server['server_uuid']) + self.controller.rename_backup_dir( + server_id, new_server_id, new_server["server_uuid"] + ) self.controller.remove_server(server_id, True) - self.redirect('/panel/dashboard') + self.redirect("/panel/dashboard") else: backup_path = svr_obj.backup_path if helper.validate_traversal(backup_path, zip_name): tempDir = helper.unzip_backup_archive(backup_path, zip_name) - new_server = self.controller.import_bedrock_zip_server(svr_obj.server_name, - tempDir, - server_data['executable'], - server_data['server_port']) + new_server = self.controller.import_bedrock_zip_server( + svr_obj.server_name, + tempDir, + server_data["executable"], + server_data["server_port"], + ) new_server_id = new_server new_server = self.controller.get_server_data(new_server) - self.controller.rename_backup_dir(server_id, new_server_id, new_server['server_uuid']) + self.controller.rename_backup_dir( + server_id, new_server_id, new_server["server_uuid"] + ) self.controller.remove_server(server_id, True) - self.redirect('/panel/dashboard') + self.redirect("/panel/dashboard") elif page == "unzip_server": - path = self.get_argument('path', None) + path = self.get_argument("path", None) if helper.check_file_exists(path): - helper.unzipServer(path, exec_user['user_id']) + helper.unzipServer(path, exec_user["user_id"]) else: - user_id = exec_user['user_id'] + user_id = exec_user["user_id"] if user_id: time.sleep(5) user_lang = self.controller.users.get_user_lang_by_id(user_id) - websocket_helper.broadcast_user(user_id, 'send_start_error',{ - 'error': translation.translate('error', 'no-file', user_lang) - }) + websocket_helper.broadcast_user( + user_id, + "send_start_error", + {"error": translation.translate("error", "no-file", user_lang)}, + ) return elif page == "backup_select": - path = self.get_argument('path', None) - helper.backup_select(path, exec_user['user_id']) + path = self.get_argument("path", None) + helper.backup_select(path, exec_user["user_id"]) return - @tornado.web.authenticated def delete(self, page): api_key, _, exec_user = self.current_user - superuser = exec_user['superuser'] + superuser = exec_user["superuser"] if api_key is not None: superuser = superuser and api_key.superuser - server_id = self.get_argument('id', None) - - + server_id = self.get_argument("id", None) permissions = { - 'Commands': Enum_Permissions_Server.Commands, - 'Terminal': Enum_Permissions_Server.Terminal, - 'Logs': Enum_Permissions_Server.Logs, - 'Schedule': Enum_Permissions_Server.Schedule, - 'Backup': Enum_Permissions_Server.Backup, - 'Files': Enum_Permissions_Server.Files, - 'Config': Enum_Permissions_Server.Config, - 'Players': Enum_Permissions_Server.Players, - } - user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id) + "Commands": Enum_Permissions_Server.Commands, + "Terminal": Enum_Permissions_Server.Terminal, + "Logs": Enum_Permissions_Server.Logs, + "Schedule": Enum_Permissions_Server.Schedule, + "Backup": Enum_Permissions_Server.Backup, + "Files": Enum_Permissions_Server.Files, + "Config": Enum_Permissions_Server.Config, + "Players": Enum_Permissions_Server.Players, + } + user_perms = self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) if page == "del_task": - if not permissions['Schedule'] in user_perms: + if not permissions["Schedule"] in user_perms: self.redirect("/panel/error?error=Unauthorized access to Tasks") else: - sch_id = self.get_argument('schedule_id', '-404') + sch_id = self.get_argument("schedule_id", "-404") self.tasks_manager.remove_job(sch_id) if page == "del_backup": - if not permissions['Backup'] in user_perms: + if not permissions["Backup"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Backups") return - file_path = helper.get_os_understandable_path(self.get_body_argument('file_path', default=None, strip=True)) - server_id = self.get_argument('id', None) + file_path = helper.get_os_understandable_path( + self.get_body_argument("file_path", default=None, strip=True) + ) + server_id = self.get_argument("id", None) console.warning(f"Delete {file_path} for server {server_id}") - if not self.check_server_id(server_id, 'del_backup'): + if not self.check_server_id(server_id, "del_backup"): return - else: server_id = bleach.clean(server_id) + else: + server_id = bleach.clean(server_id) server_info = self.controller.servers.get_server_data_by_id(server_id) - if not (helper.in_path(helper.get_os_understandable_path(server_info['path']), file_path) \ - or helper.in_path(helper.get_os_understandable_path(server_info['backup_path']), file_path)) \ - or not helper.check_file_exists(os.path.abspath(file_path)): + if not ( + helper.in_path( + helper.get_os_understandable_path(server_info["path"]), file_path + ) + or helper.in_path( + helper.get_os_understandable_path(server_info["backup_path"]), + file_path, + ) + ) or not helper.check_file_exists(os.path.abspath(file_path)): logger.warning(f"Invalid path in del_backup ajax call ({file_path})") console.warning(f"Invalid path in del_backup ajax call ({file_path})") return # Delete the file - if helper.validate_traversal(helper.get_os_understandable_path(server_info['backup_path']), file_path): + if helper.validate_traversal( + helper.get_os_understandable_path(server_info["backup_path"]), file_path + ): os.remove(file_path) elif page == "delete_server": - if not permissions['Config'] in user_perms: + if not permissions["Config"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Config") return - server_id = self.get_argument('id', None) - logger.info(f"Removing server from panel for server: {self.controller.servers.get_server_friendly_name(server_id)}") + server_id = self.get_argument("id", None) + logger.info( + f"Removing server from panel for server: {self.controller.servers.get_server_friendly_name(server_id)}" + ) server_data = self.controller.get_server_data(server_id) - server_name = server_data['server_name'] + server_name = server_data["server_name"] - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Deleted server {server_id} named {server_name}", - server_id, - self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Deleted server {server_id} named {server_name}", + server_id, + self.get_remote_ip(), + ) self.tasks_manager.remove_all_server_tasks(server_id) self.controller.remove_server(server_id, False) elif page == "delete_server_files": - if not permissions['Config'] in user_perms: + if not permissions["Config"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Config") return - server_id = self.get_argument('id', None) - logger.info(f"Removing server and all associated files for server: {self.controller.servers.get_server_friendly_name(server_id)}") + server_id = self.get_argument("id", None) + logger.info( + f"Removing server and all associated files for server: {self.controller.servers.get_server_friendly_name(server_id)}" + ) server_data = self.controller.get_server_data(server_id) - server_name = server_data['server_name'] + server_name = server_data["server_name"] - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Deleted server {server_id} named {server_name}", - server_id, - self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Deleted server {server_id} named {server_name}", + server_id, + self.get_remote_ip(), + ) self.tasks_manager.remove_all_server_tasks(server_id) self.controller.remove_server(server_id, True) def check_server_id(self, server_id, page_name): if server_id is None: - logger.warning(f"Server ID not defined in {page_name} ajax call ({server_id})") - console.warning(f"Server ID not defined in {page_name} ajax call ({server_id})") + logger.warning( + f"Server ID not defined in {page_name} ajax call ({server_id})" + ) + console.warning( + f"Server ID not defined in {page_name} ajax call ({server_id})" + ) return else: server_id = bleach.clean(server_id) # does this server id exist? if not self.controller.servers.server_id_exists(server_id): - logger.warning(f"Server ID not found in {page_name} ajax call ({server_id})") - console.warning(f"Server ID not found in {page_name} ajax call ({server_id})") + logger.warning( + f"Server ID not found in {page_name} ajax call ({server_id})" + ) + console.warning( + f"Server ID not found in {page_name} ajax call ({server_id})" + ) return return True diff --git a/app/classes/web/api_handler.py b/app/classes/web/api_handler.py index 1374d929..ae4b101f 100644 --- a/app/classes/web/api_handler.py +++ b/app/classes/web/api_handler.py @@ -4,33 +4,46 @@ import re from app.classes.web.base_handler import BaseHandler logger = logging.getLogger(__name__) -bearer_pattern = re.compile(r'^Bearer', flags=re.IGNORECASE) +bearer_pattern = re.compile(r"^Bearer", flags=re.IGNORECASE) + class ApiHandler(BaseHandler): - def return_response(self, status: int, data: dict): # Define a standardized response self.set_status(status) self.write(data) - def access_denied(self, user, reason=''): + def access_denied(self, user, reason=""): if reason: - reason = ' because ' + reason - logger.info("User %s from IP %s was denied access to the API route " + self.request.path + reason, user, self.get_remote_ip()) - self.finish(self.return_response(403, { - 'error':'ACCESS_DENIED', - 'info':'You were denied access to the requested resource' - })) + reason = " because " + reason + logger.info( + "User %s from IP %s was denied access to the API route " + + self.request.path + + reason, + user, + self.get_remote_ip(), + ) + self.finish( + self.return_response( + 403, + { + "error": "ACCESS_DENIED", + "info": "You were denied access to the requested resource", + }, + ) + ) def authenticate_user(self) -> bool: try: logger.debug("Searching for specified token") - api_token = self.get_argument('token', '') - if api_token is None and self.request.headers.get('Authorization'): - api_token = bearer_pattern.sub('', self.request.headers.get('Authorization')) + api_token = self.get_argument("token", "") + if api_token is None and self.request.headers.get("Authorization"): + api_token = bearer_pattern.sub( + "", self.request.headers.get("Authorization") + ) elif api_token is None: - api_token = self.get_cookie('token') + api_token = self.get_cookie("token") user_data = self.controller.users.get_user_by_api_token(api_token) logger.debug("Checking results") @@ -39,17 +52,22 @@ class ApiHandler(BaseHandler): logger.info(f"User {user_data['username']} has authenticated to API") # TODO: Role check - return True # This is to set the "authenticated" + return True # This is to set the "authenticated" else: logging.debug("Auth unsuccessful") self.access_denied("unknown", "the user provided an invalid token") return False except Exception as e: logger.warning("An error occured while authenticating an API user: %s", e) - self.finish(self.return_response(403, { - 'error':'ACCESS_DENIED', - 'info':'An error occured while authenticating the user' - })) + self.finish( + self.return_response( + 403, + { + "error": "ACCESS_DENIED", + "info": "An error occured while authenticating the user", + }, + ) + ) return False diff --git a/app/classes/web/base_handler.py b/app/classes/web/base_handler.py index 84cb1e4b..d8fe6c95 100644 --- a/app/classes/web/base_handler.py +++ b/app/classes/web/base_handler.py @@ -1,9 +1,5 @@ import logging -from typing import ( - Union, - List, - Optional, Tuple, Dict, Any -) +from typing import Union, List, Optional, Tuple, Dict, Any from app.classes.models.users import ApiKeys from app.classes.shared.authentication import authentication @@ -19,25 +15,33 @@ except ModuleNotFoundError as e: logger = logging.getLogger(__name__) + class BaseHandler(tornado.web.RequestHandler): nobleach = {bool, type(None)} redactables = ("pass", "api") # noinspection PyAttributeOutsideInit - def initialize(self, controller: Controller = None, tasks_manager=None, translator=None): + def initialize( + self, controller: Controller = None, tasks_manager=None, translator=None + ): self.controller = controller self.tasks_manager = tasks_manager self.translator = translator def get_remote_ip(self): - remote_ip = self.request.headers.get("X-Real-IP") or \ - self.request.headers.get("X-Forwarded-For") or \ - self.request.remote_ip + remote_ip = ( + self.request.headers.get("X-Real-IP") + or self.request.headers.get("X-Forwarded-For") + or self.request.remote_ip + ) return remote_ip current_user: Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]] - def get_current_user(self) -> Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]: + + def get_current_user( + self, + ) -> Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]: return authentication.check(self.get_cookie("token")) def autobleach(self, name, text): @@ -54,11 +58,13 @@ class BaseHandler(tornado.web.RequestHandler): return bleach.clean(text) def get_argument( - self, - name: str, - default: Union[None, str, tornado.web._ArgDefaultMarker] = tornado.web._ARG_DEFAULT, - strip: bool = True, - ) -> Optional[str]: + self, + name: str, + default: Union[ + None, str, tornado.web._ArgDefaultMarker + ] = tornado.web._ARG_DEFAULT, + strip: bool = True, + ) -> Optional[str]: arg = self._get_argument(name, default, self.request.arguments, strip) return self.autobleach(name, arg) diff --git a/app/classes/web/default_handler.py b/app/classes/web/default_handler.py index 868c4c42..8c417d21 100644 --- a/app/classes/web/default_handler.py +++ b/app/classes/web/default_handler.py @@ -4,6 +4,7 @@ from app.classes.web.base_handler import BaseHandler logger = logging.getLogger(__name__) + class DefaultHandler(BaseHandler): # Override prepare() instead of get() to cover all possible HTTP methods. @@ -18,5 +19,5 @@ class DefaultHandler(BaseHandler): else: self.redirect( "/public/login", - #translate=self.translator.translate, + # translate=self.translator.translate, ) diff --git a/app/classes/web/file_handler.py b/app/classes/web/file_handler.py index 3f84fc51..49c45a9a 100644 --- a/app/classes/web/file_handler.py +++ b/app/classes/web/file_handler.py @@ -17,8 +17,8 @@ except ModuleNotFoundError as e: logger = logging.getLogger(__name__) -class FileHandler(BaseHandler): +class FileHandler(BaseHandler): def render_page(self, template, page_data): self.render( template, @@ -29,292 +29,367 @@ class FileHandler(BaseHandler): @tornado.web.authenticated def get(self, page): api_key, _, exec_user = self.current_user - superuser = exec_user['superuser'] + superuser = exec_user["superuser"] if api_key is not None: superuser = superuser and api_key.superuser - server_id = self.get_argument('id', None) + server_id = self.get_argument("id", None) permissions = { - 'Commands': Enum_Permissions_Server.Commands, - 'Terminal': Enum_Permissions_Server.Terminal, - 'Logs': Enum_Permissions_Server.Logs, - 'Schedule': Enum_Permissions_Server.Schedule, - 'Backup': Enum_Permissions_Server.Backup, - 'Files': Enum_Permissions_Server.Files, - 'Config': Enum_Permissions_Server.Config, - 'Players': Enum_Permissions_Server.Players, - } - user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id) + "Commands": Enum_Permissions_Server.Commands, + "Terminal": Enum_Permissions_Server.Terminal, + "Logs": Enum_Permissions_Server.Logs, + "Schedule": Enum_Permissions_Server.Schedule, + "Backup": Enum_Permissions_Server.Backup, + "Files": Enum_Permissions_Server.Files, + "Config": Enum_Permissions_Server.Config, + "Players": Enum_Permissions_Server.Players, + } + user_perms = self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) if page == "get_file": - if not permissions['Files'] in user_perms: + if not permissions["Files"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Files") return - file_path = helper.get_os_understandable_path(self.get_argument('file_path', None)) + file_path = helper.get_os_understandable_path( + self.get_argument("file_path", None) + ) - if not self.check_server_id(server_id, 'get_file'): + if not self.check_server_id(server_id, "get_file"): return else: server_id = bleach.clean(server_id) - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), file_path)\ - or not helper.check_file_exists(os.path.abspath(file_path)): - logger.warning(f"Invalid path in get_file file file ajax call ({file_path})") - console.warning(f"Invalid path in get_file file file ajax call ({file_path})") + if not helper.in_path( + helper.get_os_understandable_path( + self.controller.servers.get_server_data_by_id(server_id)["path"] + ), + file_path, + ) or not helper.check_file_exists(os.path.abspath(file_path)): + logger.warning( + f"Invalid path in get_file file file ajax call ({file_path})" + ) + console.warning( + f"Invalid path in get_file file file ajax call ({file_path})" + ) return - error = None try: - with open(file_path, encoding='utf-8') as file: + with open(file_path, encoding="utf-8") as file: file_contents = file.read() except UnicodeDecodeError: - file_contents = '' - error = 'UnicodeDecodeError' + file_contents = "" + error = "UnicodeDecodeError" - self.write({ - 'content': file_contents, - 'error': error - }) + self.write({"content": file_contents, "error": error}) self.finish() elif page == "get_tree": - if not permissions['Files'] in user_perms: + if not permissions["Files"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Files") return - path = self.get_argument('path', None) + path = self.get_argument("path", None) - if not self.check_server_id(server_id, 'get_tree'): + if not self.check_server_id(server_id, "get_tree"): return else: server_id = bleach.clean(server_id) - if helper.validate_traversal(self.controller.servers.get_server_data_by_id(server_id)['path'], path): - self.write(helper.get_os_understandable_path(path) + '\n' + - helper.generate_tree(path)) + if helper.validate_traversal( + self.controller.servers.get_server_data_by_id(server_id)["path"], path + ): + self.write( + helper.get_os_understandable_path(path) + + "\n" + + helper.generate_tree(path) + ) self.finish() elif page == "get_dir": - if not permissions['Files'] in user_perms: + if not permissions["Files"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Files") return - path = self.get_argument('path', None) + path = self.get_argument("path", None) - if not self.check_server_id(server_id, 'get_tree'): + if not self.check_server_id(server_id, "get_tree"): return else: server_id = bleach.clean(server_id) - if helper.validate_traversal(self.controller.servers.get_server_data_by_id(server_id)['path'], path): - self.write(helper.get_os_understandable_path(path) + '\n' + - helper.generate_dir(path)) + if helper.validate_traversal( + self.controller.servers.get_server_data_by_id(server_id)["path"], path + ): + self.write( + helper.get_os_understandable_path(path) + + "\n" + + helper.generate_dir(path) + ) self.finish() @tornado.web.authenticated def post(self, page): api_key, _, exec_user = self.current_user - superuser = exec_user['superuser'] + superuser = exec_user["superuser"] if api_key is not None: superuser = superuser and api_key.superuser - server_id = self.get_argument('id', None) + server_id = self.get_argument("id", None) permissions = { - 'Commands': Enum_Permissions_Server.Commands, - 'Terminal': Enum_Permissions_Server.Terminal, - 'Logs': Enum_Permissions_Server.Logs, - 'Schedule': Enum_Permissions_Server.Schedule, - 'Backup': Enum_Permissions_Server.Backup, - 'Files': Enum_Permissions_Server.Files, - 'Config': Enum_Permissions_Server.Config, - 'Players': Enum_Permissions_Server.Players, - } - user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id) + "Commands": Enum_Permissions_Server.Commands, + "Terminal": Enum_Permissions_Server.Terminal, + "Logs": Enum_Permissions_Server.Logs, + "Schedule": Enum_Permissions_Server.Schedule, + "Backup": Enum_Permissions_Server.Backup, + "Files": Enum_Permissions_Server.Files, + "Config": Enum_Permissions_Server.Config, + "Players": Enum_Permissions_Server.Players, + } + user_perms = self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) if page == "create_file": - if not permissions['Files'] in user_perms: + if not permissions["Files"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Files") return - file_parent = helper.get_os_understandable_path(self.get_body_argument('file_parent', default=None, strip=True)) - file_name = self.get_body_argument('file_name', default=None, strip=True) + file_parent = helper.get_os_understandable_path( + self.get_body_argument("file_parent", default=None, strip=True) + ) + file_name = self.get_body_argument("file_name", default=None, strip=True) file_path = os.path.join(file_parent, file_name) - if not self.check_server_id(server_id, 'create_file'): + if not self.check_server_id(server_id, "create_file"): return else: server_id = bleach.clean(server_id) - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), file_path) \ - or helper.check_file_exists(os.path.abspath(file_path)): - logger.warning(f"Invalid path in create_file file ajax call ({file_path})") - console.warning(f"Invalid path in create_file file ajax call ({file_path})") + if not helper.in_path( + helper.get_os_understandable_path( + self.controller.servers.get_server_data_by_id(server_id)["path"] + ), + file_path, + ) or helper.check_file_exists(os.path.abspath(file_path)): + logger.warning( + f"Invalid path in create_file file ajax call ({file_path})" + ) + console.warning( + f"Invalid path in create_file file ajax call ({file_path})" + ) return # Create the file by opening it - with open(file_path, 'w', encoding='utf-8') as file_object: + with open(file_path, "w", encoding="utf-8") as file_object: file_object.close() elif page == "create_dir": - if not permissions['Files'] in user_perms: + if not permissions["Files"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Files") return - dir_parent = helper.get_os_understandable_path(self.get_body_argument('dir_parent', default=None, strip=True)) - dir_name = self.get_body_argument('dir_name', default=None, strip=True) + dir_parent = helper.get_os_understandable_path( + self.get_body_argument("dir_parent", default=None, strip=True) + ) + dir_name = self.get_body_argument("dir_name", default=None, strip=True) dir_path = os.path.join(dir_parent, dir_name) - if not self.check_server_id(server_id, 'create_dir'): + if not self.check_server_id(server_id, "create_dir"): return else: server_id = bleach.clean(server_id) - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), dir_path) \ - or helper.check_path_exists(os.path.abspath(dir_path)): - logger.warning(f"Invalid path in create_dir file ajax call ({dir_path})") - console.warning(f"Invalid path in create_dir file ajax call ({dir_path})") + if not helper.in_path( + helper.get_os_understandable_path( + self.controller.servers.get_server_data_by_id(server_id)["path"] + ), + dir_path, + ) or helper.check_path_exists(os.path.abspath(dir_path)): + logger.warning( + f"Invalid path in create_dir file ajax call ({dir_path})" + ) + console.warning( + f"Invalid path in create_dir file ajax call ({dir_path})" + ) return # Create the directory os.mkdir(dir_path) elif page == "unzip_file": - if not permissions['Files'] in user_perms: + if not permissions["Files"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Files") return - path = helper.get_os_understandable_path(self.get_argument('path', None)) + path = helper.get_os_understandable_path(self.get_argument("path", None)) helper.unzipFile(path) self.redirect(f"/panel/server_detail?id={server_id}&subpage=files") return - @tornado.web.authenticated def delete(self, page): api_key, _, exec_user = self.current_user - superuser = exec_user['superuser'] + superuser = exec_user["superuser"] if api_key is not None: superuser = superuser and api_key.superuser - server_id = self.get_argument('id', None) + server_id = self.get_argument("id", None) permissions = { - 'Commands': Enum_Permissions_Server.Commands, - 'Terminal': Enum_Permissions_Server.Terminal, - 'Logs': Enum_Permissions_Server.Logs, - 'Schedule': Enum_Permissions_Server.Schedule, - 'Backup': Enum_Permissions_Server.Backup, - 'Files': Enum_Permissions_Server.Files, - 'Config': Enum_Permissions_Server.Config, - 'Players': Enum_Permissions_Server.Players, - } - user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id) + "Commands": Enum_Permissions_Server.Commands, + "Terminal": Enum_Permissions_Server.Terminal, + "Logs": Enum_Permissions_Server.Logs, + "Schedule": Enum_Permissions_Server.Schedule, + "Backup": Enum_Permissions_Server.Backup, + "Files": Enum_Permissions_Server.Files, + "Config": Enum_Permissions_Server.Config, + "Players": Enum_Permissions_Server.Players, + } + user_perms = self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) if page == "del_file": - if not permissions['Files'] in user_perms: + if not permissions["Files"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Files") return - file_path = helper.get_os_understandable_path(self.get_body_argument('file_path', default=None, strip=True)) + file_path = helper.get_os_understandable_path( + self.get_body_argument("file_path", default=None, strip=True) + ) console.warning(f"Delete {file_path} for server {server_id}") - if not self.check_server_id(server_id, 'del_file'): + if not self.check_server_id(server_id, "del_file"): return - else: server_id = bleach.clean(server_id) + else: + server_id = bleach.clean(server_id) server_info = self.controller.servers.get_server_data_by_id(server_id) - if not (helper.in_path(helper.get_os_understandable_path(server_info['path']), file_path) \ - or helper.in_path(helper.get_os_understandable_path(server_info['backup_path']), file_path)) \ - or not helper.check_file_exists(os.path.abspath(file_path)): + if not ( + helper.in_path( + helper.get_os_understandable_path(server_info["path"]), file_path + ) + or helper.in_path( + helper.get_os_understandable_path(server_info["backup_path"]), + file_path, + ) + ) or not helper.check_file_exists(os.path.abspath(file_path)): logger.warning(f"Invalid path in del_file file ajax call ({file_path})") - console.warning(f"Invalid path in del_file file ajax call ({file_path})") + console.warning( + f"Invalid path in del_file file ajax call ({file_path})" + ) return # Delete the file file_helper.del_file(file_path) elif page == "del_dir": - if not permissions['Files'] in user_perms: + if not permissions["Files"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Files") return - dir_path = helper.get_os_understandable_path(self.get_body_argument('dir_path', default=None, strip=True)) + dir_path = helper.get_os_understandable_path( + self.get_body_argument("dir_path", default=None, strip=True) + ) console.warning(f"Delete {dir_path} for server {server_id}") - if not self.check_server_id(server_id, 'del_dir'): + if not self.check_server_id(server_id, "del_dir"): return else: server_id = bleach.clean(server_id) server_info = self.controller.servers.get_server_data_by_id(server_id) - if not helper.in_path(helper.get_os_understandable_path(server_info['path']), dir_path) \ - or not helper.check_path_exists(os.path.abspath(dir_path)): + if not helper.in_path( + helper.get_os_understandable_path(server_info["path"]), dir_path + ) or not helper.check_path_exists(os.path.abspath(dir_path)): logger.warning(f"Invalid path in del_file file ajax call ({dir_path})") console.warning(f"Invalid path in del_file file ajax call ({dir_path})") return # Delete the directory # os.rmdir(dir_path) # Would only remove empty directories - if helper.validate_traversal(helper.get_os_understandable_path(server_info['path']), dir_path): + if helper.validate_traversal( + helper.get_os_understandable_path(server_info["path"]), dir_path + ): # Removes also when there are contents file_helper.del_dirs(dir_path) @tornado.web.authenticated def put(self, page): api_key, _, exec_user = self.current_user - superuser = exec_user['superuser'] + superuser = exec_user["superuser"] if api_key is not None: superuser = superuser and api_key.superuser - server_id = self.get_argument('id', None) + server_id = self.get_argument("id", None) permissions = { - 'Commands': Enum_Permissions_Server.Commands, - 'Terminal': Enum_Permissions_Server.Terminal, - 'Logs': Enum_Permissions_Server.Logs, - 'Schedule': Enum_Permissions_Server.Schedule, - 'Backup': Enum_Permissions_Server.Backup, - 'Files': Enum_Permissions_Server.Files, - 'Config': Enum_Permissions_Server.Config, - 'Players': Enum_Permissions_Server.Players, - } - user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id) + "Commands": Enum_Permissions_Server.Commands, + "Terminal": Enum_Permissions_Server.Terminal, + "Logs": Enum_Permissions_Server.Logs, + "Schedule": Enum_Permissions_Server.Schedule, + "Backup": Enum_Permissions_Server.Backup, + "Files": Enum_Permissions_Server.Files, + "Config": Enum_Permissions_Server.Config, + "Players": Enum_Permissions_Server.Players, + } + user_perms = self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) if page == "save_file": - if not permissions['Files'] in user_perms: + if not permissions["Files"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Files") return - file_contents = self.get_body_argument('file_contents', default=None, strip=True) - file_path = helper.get_os_understandable_path(self.get_body_argument('file_path', default=None, strip=True)) + file_contents = self.get_body_argument( + "file_contents", default=None, strip=True + ) + file_path = helper.get_os_understandable_path( + self.get_body_argument("file_path", default=None, strip=True) + ) - if not self.check_server_id(server_id, 'save_file'): + if not self.check_server_id(server_id, "save_file"): return else: server_id = bleach.clean(server_id) - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), file_path)\ - or not helper.check_file_exists(os.path.abspath(file_path)): - logger.warning(f"Invalid path in save_file file ajax call ({file_path})") - console.warning(f"Invalid path in save_file file ajax call ({file_path})") + if not helper.in_path( + helper.get_os_understandable_path( + self.controller.servers.get_server_data_by_id(server_id)["path"] + ), + file_path, + ) or not helper.check_file_exists(os.path.abspath(file_path)): + logger.warning( + f"Invalid path in save_file file ajax call ({file_path})" + ) + console.warning( + f"Invalid path in save_file file ajax call ({file_path})" + ) return # Open the file in write mode and store the content in file_object - with open(file_path, 'w', encoding='utf-8') as file_object: + with open(file_path, "w", encoding="utf-8") as file_object: file_object.write(file_contents) elif page == "rename_file": - if not permissions['Files'] in user_perms: + if not permissions["Files"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Files") return - item_path = helper.get_os_understandable_path(self.get_body_argument('item_path', default=None, strip=True)) - new_item_name = self.get_body_argument('new_item_name', default=None, strip=True) + item_path = helper.get_os_understandable_path( + self.get_body_argument("item_path", default=None, strip=True) + ) + new_item_name = self.get_body_argument( + "new_item_name", default=None, strip=True + ) - if not self.check_server_id(server_id, 'rename_file'): + if not self.check_server_id(server_id, "rename_file"): return else: server_id = bleach.clean(server_id) @@ -324,53 +399,73 @@ class FileHandler(BaseHandler): console.warning("Invalid path(s) in rename_file file ajax call") return - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), item_path) \ - or not helper.check_path_exists(os.path.abspath(item_path)): - logger.warning(f"Invalid old name path in rename_file file ajax call ({server_id})") - console.warning(f"Invalid old name path in rename_file file ajax call ({server_id})") + if not helper.in_path( + helper.get_os_understandable_path( + self.controller.servers.get_server_data_by_id(server_id)["path"] + ), + item_path, + ) or not helper.check_path_exists(os.path.abspath(item_path)): + logger.warning( + f"Invalid old name path in rename_file file ajax call ({server_id})" + ) + console.warning( + f"Invalid old name path in rename_file file ajax call ({server_id})" + ) return new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name) - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), - new_item_path) \ - or helper.check_path_exists(os.path.abspath(new_item_path)): - logger.warning(f"Invalid new name path in rename_file file ajax call ({server_id})") - console.warning(f"Invalid new name path in rename_file file ajax call ({server_id})") + if not helper.in_path( + helper.get_os_understandable_path( + self.controller.servers.get_server_data_by_id(server_id)["path"] + ), + new_item_path, + ) or helper.check_path_exists(os.path.abspath(new_item_path)): + logger.warning( + f"Invalid new name path in rename_file file ajax call ({server_id})" + ) + console.warning( + f"Invalid new name path in rename_file file ajax call ({server_id})" + ) return # RENAME os.rename(item_path, new_item_path) - @tornado.web.authenticated def patch(self, page): api_key, _, exec_user = self.current_user - superuser = exec_user['superuser'] + superuser = exec_user["superuser"] if api_key is not None: superuser = superuser and api_key.superuser - server_id = self.get_argument('id', None) + server_id = self.get_argument("id", None) permissions = { - 'Commands': Enum_Permissions_Server.Commands, - 'Terminal': Enum_Permissions_Server.Terminal, - 'Logs': Enum_Permissions_Server.Logs, - 'Schedule': Enum_Permissions_Server.Schedule, - 'Backup': Enum_Permissions_Server.Backup, - 'Files': Enum_Permissions_Server.Files, - 'Config': Enum_Permissions_Server.Config, - 'Players': Enum_Permissions_Server.Players, - } - user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id) + "Commands": Enum_Permissions_Server.Commands, + "Terminal": Enum_Permissions_Server.Terminal, + "Logs": Enum_Permissions_Server.Logs, + "Schedule": Enum_Permissions_Server.Schedule, + "Backup": Enum_Permissions_Server.Backup, + "Files": Enum_Permissions_Server.Files, + "Config": Enum_Permissions_Server.Config, + "Players": Enum_Permissions_Server.Players, + } + user_perms = self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) if page == "rename_file": - if not permissions['Files'] in user_perms: + if not permissions["Files"] in user_perms: if not superuser: self.redirect("/panel/error?error=Unauthorized access to Files") return - item_path = helper.get_os_understandable_path(self.get_body_argument('item_path', default=None, strip=True)) - new_item_name = self.get_body_argument('new_item_name', default=None, strip=True) + item_path = helper.get_os_understandable_path( + self.get_body_argument("item_path", default=None, strip=True) + ) + new_item_name = self.get_body_argument( + "new_item_name", default=None, strip=True + ) - if not self.check_server_id(server_id, 'rename_file'): + if not self.check_server_id(server_id, "rename_file"): return else: server_id = bleach.clean(server_id) @@ -380,19 +475,34 @@ class FileHandler(BaseHandler): console.warning("Invalid path(s) in rename_file file ajax call") return - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), item_path) \ - or not helper.check_path_exists(os.path.abspath(item_path)): - logger.warning(f"Invalid old name path in rename_file file ajax call ({server_id})") - console.warning(f"Invalid old name path in rename_file file ajax call ({server_id})") + if not helper.in_path( + helper.get_os_understandable_path( + self.controller.servers.get_server_data_by_id(server_id)["path"] + ), + item_path, + ) or not helper.check_path_exists(os.path.abspath(item_path)): + logger.warning( + f"Invalid old name path in rename_file file ajax call ({server_id})" + ) + console.warning( + f"Invalid old name path in rename_file file ajax call ({server_id})" + ) return new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name) - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), - new_item_path) \ - or helper.check_path_exists(os.path.abspath(new_item_path)): - logger.warning(f"Invalid new name path in rename_file file ajax call ({server_id})") - console.warning(f"Invalid new name path in rename_file file ajax call ({server_id})") + if not helper.in_path( + helper.get_os_understandable_path( + self.controller.servers.get_server_data_by_id(server_id)["path"] + ), + new_item_path, + ) or helper.check_path_exists(os.path.abspath(new_item_path)): + logger.warning( + f"Invalid new name path in rename_file file ajax call ({server_id})" + ) + console.warning( + f"Invalid new name path in rename_file file ajax call ({server_id})" + ) return # RENAME @@ -400,15 +510,23 @@ class FileHandler(BaseHandler): def check_server_id(self, server_id, page_name): if server_id is None: - logger.warning(f"Server ID not defined in {page_name} file ajax call ({server_id})") - console.warning(f"Server ID not defined in {page_name} file ajax call ({server_id})") + logger.warning( + f"Server ID not defined in {page_name} file ajax call ({server_id})" + ) + console.warning( + f"Server ID not defined in {page_name} file ajax call ({server_id})" + ) return else: server_id = bleach.clean(server_id) # does this server id exist? if not self.controller.servers.server_id_exists(server_id): - logger.warning(f"Server ID not found in {page_name} file ajax call ({server_id})") - console.warning(f"Server ID not found in {page_name} file ajax call ({server_id})") + logger.warning( + f"Server ID not found in {page_name} file ajax call ({server_id})" + ) + console.warning( + f"Server ID not found in {page_name} file ajax call ({server_id})" + ) return return True diff --git a/app/classes/web/http_handler.py b/app/classes/web/http_handler.py index e3b03f57..e772b007 100644 --- a/app/classes/web/http_handler.py +++ b/app/classes/web/http_handler.py @@ -11,22 +11,23 @@ except ModuleNotFoundError as e: logger = logging.getLogger(__name__) + class HTTPHandler(BaseHandler): def get(self): url = str(self.request.host) port = 443 url_list = url.split(":") if url_list[0] != "": - url = 'https://' + url_list[0] + url = "https://" + url_list[0] else: - url = 'https://' + url - db_port = helper.get_setting('https_port') + url = "https://" + url + db_port = helper.get_setting("https_port") try: resp = requests.get(url + ":" + str(port)) resp.raise_for_status() except Exception: port = db_port - self.redirect(url+":"+str(port)) + self.redirect(url + ":" + str(port)) class HTTPHandlerPage(BaseHandler): @@ -35,13 +36,13 @@ class HTTPHandlerPage(BaseHandler): port = 443 url_list = url.split(":") if url_list[0] != "": - url = 'https://' + url_list[0] + url = "https://" + url_list[0] else: - url = 'https://' + url - db_port = helper.get_setting('https_port') + url = "https://" + url + db_port = helper.get_setting("https_port") try: resp = requests.get(url + ":" + str(port)) resp.raise_for_status() except Exception: port = db_port - self.redirect(url+":"+str(port)) + self.redirect(url + ":" + str(port)) diff --git a/app/classes/web/http_handler_page.py b/app/classes/web/http_handler_page.py index 231da39b..96f8a611 100644 --- a/app/classes/web/http_handler_page.py +++ b/app/classes/web/http_handler_page.py @@ -5,22 +5,24 @@ from app.classes.shared.helpers import helper from app.classes.web.base_handler import BaseHandler logger = logging.getLogger(__name__) + + class HTTPHandlerPage(BaseHandler): def get(self): url = self.request.full_url port = 443 - if url[len(url)-1] == '/': - url = url.strip(url[len(url)-1]) - url_list = url.split('/') + if url[len(url) - 1] == "/": + url = url.strip(url[len(url) - 1]) + url_list = url.split("/") if url_list[0] != "": - primary_url = url_list[0] + ":"+str(port)+"/" - backup_url = url_list[0] + ":" +str(helper.get_setting("https_port")) +"/" - for i in range(len(url_list)-1): - primary_url += url_list[i+1] - backup_url += url_list[i+1] + primary_url = url_list[0] + ":" + str(port) + "/" + backup_url = url_list[0] + ":" + str(helper.get_setting("https_port")) + "/" + for i in range(len(url_list) - 1): + primary_url += url_list[i + 1] + backup_url += url_list[i + 1] else: primary_url = url + str(port) - backup_url = url + str(helper.get_setting('https_port')) + backup_url = url + str(helper.get_setting("https_port")) try: resp = requests.get(primary_url) @@ -28,4 +30,4 @@ class HTTPHandlerPage(BaseHandler): url = primary_url except Exception: url = backup_url - self.redirect('https://'+url+':'+ str(port)) + self.redirect("https://" + url + ":" + str(port)) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 3ceb38e1..fd859ae1 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -20,7 +20,8 @@ try: import tornado.web import tornado.escape from tornado import iostream - #TZLocal is set as a hidden import on win pipeline + + # TZLocal is set as a hidden import on win pipeline from tzlocal import get_localzone from cron_validator import CronValidator @@ -29,8 +30,8 @@ except ModuleNotFoundError as ex: logger = logging.getLogger(__name__) -class PanelHandler(BaseHandler): +class PanelHandler(BaseHandler): def get_user_roles(self) -> Dict[str, list]: user_roles = {} for user in self.controller.users.get_all_users(): @@ -42,30 +43,40 @@ class PanelHandler(BaseHandler): def get_role_servers(self) -> set: servers = set() for server in self.controller.list_defined_servers(): - argument = int(float( - bleach.clean( - self.get_argument(f"server_{server['server_id']}_access", '0') + argument = int( + float( + bleach.clean( + self.get_argument(f"server_{server['server_id']}_access", "0") + ) ) - )) + ) if argument: - servers.add(server['server_id']) + servers.add(server["server_id"]) return servers def get_perms_quantity(self) -> Tuple[str, dict]: permissions_mask: str = "000" server_quantity: dict = {} - for permission in self.controller.crafty_perms.list_defined_crafty_permissions(): - argument = int(float(bleach.clean( - self.get_argument(f'permission_{permission.name}', '0') - ))) - if argument: - permissions_mask = self.controller.crafty_perms.set_permission(permissions_mask, permission, argument) - - q_argument = int(float( - bleach.clean( - self.get_argument(f'quantity_{permission.name}', '0') + for ( + permission + ) in self.controller.crafty_perms.list_defined_crafty_permissions(): + argument = int( + float( + bleach.clean( + self.get_argument(f"permission_{permission.name}", "0") + ) ) - )) + ) + if argument: + permissions_mask = self.controller.crafty_perms.set_permission( + permissions_mask, permission, argument + ) + + q_argument = int( + float( + bleach.clean(self.get_argument(f"quantity_{permission.name}", "0")) + ) + ) if q_argument: server_quantity[permission.name] = q_argument else: @@ -74,35 +85,39 @@ class PanelHandler(BaseHandler): def get_perms(self) -> str: permissions_mask: str = "000" - for permission in self.controller.crafty_perms.list_defined_crafty_permissions(): - argument = self.get_argument(f'permission_{permission.name}', None) + for ( + permission + ) in self.controller.crafty_perms.list_defined_crafty_permissions(): + argument = self.get_argument(f"permission_{permission.name}", None) if argument is not None: - permissions_mask = self.controller.crafty_perms.set_permission(permissions_mask, permission, - 1 if argument == '1' else 0) + permissions_mask = self.controller.crafty_perms.set_permission( + permissions_mask, permission, 1 if argument == "1" else 0 + ) return permissions_mask def get_perms_server(self) -> str: permissions_mask = "00000000" for permission in self.controller.server_perms.list_defined_permissions(): - argument = self.get_argument(f'permission_{permission.name}', None) + argument = self.get_argument(f"permission_{permission.name}", None) if argument is not None: - permissions_mask = self.controller.server_perms.set_permission(permissions_mask, permission, - 1 if argument == '1' else 0) + permissions_mask = self.controller.server_perms.set_permission( + permissions_mask, permission, 1 if argument == "1" else 0 + ) return permissions_mask def get_user_role_memberships(self) -> set: roles = set() for role in self.controller.roles.get_all_roles(): - if self.get_argument(f'role_{role.role_id}_membership', None) == '1': + if self.get_argument(f"role_{role.role_id}_membership", None) == "1": roles.add(role.role_id) return roles def download_file(self, name: str, file: str): - self.set_header('Content-Type', 'application/octet-stream') - self.set_header('Content-Disposition', f'attachment; filename={name}') + self.set_header("Content-Type", "application/octet-stream") + self.set_header("Content-Disposition", f"attachment; filename={name}") chunk_size = 1024 * 1024 * 4 # 4 MiB - with open(file, 'rb') as f: + with open(file, "rb") as f: while True: chunk = f.read(chunk_size) if not chunk: @@ -122,10 +137,10 @@ class PanelHandler(BaseHandler): del chunk def check_server_id(self): - server_id = self.get_argument('id', None) + server_id = self.get_argument("id", None) api_key, _, exec_user = self.current_user - superuser = exec_user['superuser'] + superuser = exec_user["superuser"] # Commented out because there is no server access control for API keys, they just inherit from the host user # if api_key is not None: @@ -141,14 +156,20 @@ class PanelHandler(BaseHandler): return None # Does the user have permission? - if not superuser: # TODO: Figure out a better solution + if not superuser: # TODO: Figure out a better solution if api_key is not None: - if not self.controller.servers.server_id_authorized_api_key(server_id, api_key): - print(f'API key {api_key.name} (id: {api_key.token_id}) does not have permission') + if not self.controller.servers.server_id_authorized_api_key( + server_id, api_key + ): + print( + f"API key {api_key.name} (id: {api_key.token_id}) does not have permission" + ) self.redirect("/panel/error?error=Invalid Server ID") return None else: - if not self.controller.servers.server_id_authorized(server_id, exec_user["user_id"]): + if not self.controller.servers.server_id_authorized( + server_id, exec_user["user_id"] + ): print(f'User {exec_user["user_id"]} does not have permission') self.redirect("/pandel/error?error=Invalid Server ID") return None @@ -158,120 +179,145 @@ class PanelHandler(BaseHandler): # TODO: Make the related front-end elements update with AJAX def fetch_server_data(self, page_data): total_players = 0 - for server in page_data['servers']: - total_players += len(self.controller.stats.get_server_players(server['server_data']['server_id'])) - page_data['num_players'] = total_players + for server in page_data["servers"]: + total_players += len( + self.controller.stats.get_server_players( + server["server_data"]["server_id"] + ) + ) + page_data["num_players"] = total_players - for s in page_data['servers']: + for s in page_data["servers"]: try: - data = json.loads(s['int_ping_results']) - s['int_ping_results'] = data + data = json.loads(s["int_ping_results"]) + s["int_ping_results"] = data except Exception as e: logger.error(f"Failed server data for page with error: {e}") return page_data - @tornado.web.authenticated async def get(self, page): - error = self.get_argument('error', "WTF Error!") + error = self.get_argument("error", "WTF Error!") template = "panel/denied.html" now = time.time() - formatted_time = str(datetime.datetime.fromtimestamp(now).strftime('%Y-%m-%d %H:%M:%S')) + formatted_time = str( + datetime.datetime.fromtimestamp(now).strftime("%Y-%m-%d %H:%M:%S") + ) api_key, _token_data, exec_user = self.current_user - superuser = exec_user['superuser'] + superuser = exec_user["superuser"] if api_key is not None: superuser = superuser and api_key.superuser exec_user_role = set() - if superuser: # TODO: Figure out a better solution + if superuser: # TODO: Figure out a better solution defined_servers = self.controller.list_defined_servers() exec_user_role.add("Super User") - exec_user_crafty_permissions = self.controller.crafty_perms.list_defined_crafty_permissions() + exec_user_crafty_permissions = ( + self.controller.crafty_perms.list_defined_crafty_permissions() + ) else: if api_key is not None: - exec_user_crafty_permissions = self.controller.crafty_perms.get_api_key_permissions_list(api_key) + exec_user_crafty_permissions = ( + self.controller.crafty_perms.get_api_key_permissions_list(api_key) + ) else: - exec_user_crafty_permissions = self.controller.crafty_perms.get_crafty_permissions_list( - exec_user["user_id"]) - logger.debug(exec_user['roles']) - for r in exec_user['roles']: + exec_user_crafty_permissions = ( + self.controller.crafty_perms.get_crafty_permissions_list( + exec_user["user_id"] + ) + ) + logger.debug(exec_user["roles"]) + for r in exec_user["roles"]: role = self.controller.roles.get_role(r) - exec_user_role.add(role['role_name']) - defined_servers = self.controller.servers.get_authorized_servers(exec_user["user_id"]) + exec_user_role.add(role["role_name"]) + defined_servers = self.controller.servers.get_authorized_servers( + exec_user["user_id"] + ) - - user_order = self.controller.users.get_user_by_id(exec_user['user_id']) - user_order = user_order['server_order'].split(',') + user_order = self.controller.users.get_user_by_id(exec_user["user_id"]) + user_order = user_order["server_order"].split(",") page_servers = [] server_ids = [] for server_id in user_order[:]: for server in defined_servers[:]: - if str(server['server_id']) == str(server_id): + if str(server["server_id"]) == str(server_id): page_servers.append(server) user_order.remove(server_id) defined_servers.remove(server) for server in defined_servers: - server_ids.append(str(server['server_id'])) + server_ids.append(str(server["server_id"])) if server not in page_servers: page_servers.append(server) for server_id in user_order[:]: - #remove IDs in list that user no longer has access to + # remove IDs in list that user no longer has access to if str(server_id) not in server_ids: user_order.remove(server_id) defined_servers = page_servers - page_data: Dict[str, Any] = { # todo: make this actually pull and compare version data - 'update_available': False, - 'serverTZ':get_localzone(), - 'version_data': helper.get_version_string(), - 'user_data': exec_user, - 'user_role' : exec_user_role, - 'user_crafty_permissions' : exec_user_crafty_permissions, - 'crafty_permissions': { - 'Server_Creation': Enum_Permissions_Crafty.Server_Creation, - 'User_Config': Enum_Permissions_Crafty.User_Config, - 'Roles_Config': Enum_Permissions_Crafty.Roles_Config, + "update_available": False, + "serverTZ": get_localzone(), + "version_data": helper.get_version_string(), + "user_data": exec_user, + "user_role": exec_user_role, + "user_crafty_permissions": exec_user_crafty_permissions, + "crafty_permissions": { + "Server_Creation": Enum_Permissions_Crafty.Server_Creation, + "User_Config": Enum_Permissions_Crafty.User_Config, + "Roles_Config": Enum_Permissions_Crafty.Roles_Config, }, - 'server_stats': { - 'total': len(defined_servers), - 'running': len(self.controller.list_running_servers()), - 'stopped': (len(self.controller.list_defined_servers()) - len(self.controller.list_running_servers())) + "server_stats": { + "total": len(defined_servers), + "running": len(self.controller.list_running_servers()), + "stopped": ( + len(self.controller.list_defined_servers()) + - len(self.controller.list_running_servers()) + ), }, - 'menu_servers': defined_servers, - 'hosts_data': self.controller.management.get_latest_hosts_stats(), - 'show_contribute': helper.get_setting("show_contribute_link", True), - 'error': error, - 'time': formatted_time, - 'lang': self.controller.users.get_user_lang_by_id(exec_user["user_id"]), - 'lang_page': helper.getLangPage(self.controller.users.get_user_lang_by_id(exec_user["user_id"])), - 'super_user': superuser, - 'api_key': { - 'name': api_key.name, - 'created': api_key.created, - 'server_permissions': api_key.server_permissions, - 'crafty_permissions': api_key.crafty_permissions, - 'superuser': api_key.superuser - } if api_key is not None else None, - 'superuser': superuser + "menu_servers": defined_servers, + "hosts_data": self.controller.management.get_latest_hosts_stats(), + "show_contribute": helper.get_setting("show_contribute_link", True), + "error": error, + "time": formatted_time, + "lang": self.controller.users.get_user_lang_by_id(exec_user["user_id"]), + "lang_page": helper.getLangPage( + self.controller.users.get_user_lang_by_id(exec_user["user_id"]) + ), + "super_user": superuser, + "api_key": { + "name": api_key.name, + "created": api_key.created, + "server_permissions": api_key.server_permissions, + "crafty_permissions": api_key.crafty_permissions, + "superuser": api_key.superuser, + } + if api_key is not None + else None, + "superuser": superuser, } - if helper.get_setting("allow_nsfw_profile_pictures"): + if helper.get_setting("allow_nsfw_profile_pictures"): rating = "x" else: rating = "g" - - #Get grvatar hash for profile pictures - if exec_user['email'] != 'default@example.com' or "": - g = libgravatar.Gravatar(libgravatar.sanitize_email(exec_user['email'])) - url = g.get_image(size=80, default="404", force_default=False, rating=rating, filetype_extension=False, use_ssl=True) # + "?d=404" + # Get grvatar hash for profile pictures + if exec_user["email"] != "default@example.com" or "": + g = libgravatar.Gravatar(libgravatar.sanitize_email(exec_user["email"])) + url = g.get_image( + size=80, + default="404", + force_default=False, + rating=rating, + filetype_extension=False, + use_ssl=True, + ) # + "?d=404" if requests.head(url).status_code != 404: profile_url = url else: @@ -279,18 +325,20 @@ class PanelHandler(BaseHandler): else: profile_url = "/static/assets/images/faces-clipart/pic-3.png" - page_data['user_image'] = profile_url + page_data["user_image"] = profile_url - if page == 'unauthorized': + if page == "unauthorized": template = "panel/denied.html" elif page == "error": template = "public/error.html" - elif page == 'credits': - with open(helper.credits_cache, encoding='utf-8') as credits_default_local: + elif page == "credits": + with open(helper.credits_cache, encoding="utf-8") as credits_default_local: try: - remote = requests.get('https://craftycontrol.com/credits', allow_redirects=True) + remote = requests.get( + "https://craftycontrol.com/credits", allow_redirects=True + ) credits_dict: dict = remote.json() if not credits_dict["staff"]: logger.error("Issue with upstream Staff, using local.") @@ -304,197 +352,291 @@ class PanelHandler(BaseHandler): page_data["staff"] = credits_dict["staff"] # Filter Language keys to exclude joke prefix '*' - clean_dict = {user.replace('*', ''): translation for user, translation in credits_dict['translations'].items()} + clean_dict = { + user.replace("*", ""): translation + for user, translation in credits_dict["translations"].items() + } page_data["translations"] = clean_dict # 0 Defines if we are using local credits file andd displays sadcat. if timestamp == 0: - page_data["lastUpdate"] = '๐Ÿ˜ฟ' + page_data["lastUpdate"] = "๐Ÿ˜ฟ" else: - page_data["lastUpdate"] = str(datetime.datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')) + page_data["lastUpdate"] = str( + datetime.datetime.fromtimestamp(timestamp).strftime( + "%Y-%m-%d %H:%M:%S" + ) + ) template = "panel/credits.html" - elif page == 'contribute': + elif page == "contribute": template = "panel/contribute.html" - elif page == 'dashboard': - if superuser: # TODO: Figure out a better solution + elif page == "dashboard": + if superuser: # TODO: Figure out a better solution try: - page_data['servers'] = self.controller.servers.get_all_servers_stats() + page_data[ + "servers" + ] = self.controller.servers.get_all_servers_stats() except IndexError: self.controller.stats.record_stats() - page_data['servers'] = self.controller.servers.get_all_servers_stats() + page_data[ + "servers" + ] = self.controller.servers.get_all_servers_stats() else: try: - user_auth = self.controller.servers.get_authorized_servers_stats(exec_user["user_id"]) + user_auth = self.controller.servers.get_authorized_servers_stats( + exec_user["user_id"] + ) except IndexError: self.controller.stats.record_stats() - user_auth = self.controller.servers.get_authorized_servers_stats(exec_user["user_id"]) + user_auth = self.controller.servers.get_authorized_servers_stats( + exec_user["user_id"] + ) logger.debug(f"ASFR: {user_auth}") - page_data['servers'] = user_auth - page_data['server_stats']['running'] = len( - list(filter(lambda x: x['stats']['running'], page_data['servers']))) - page_data['server_stats']['stopped'] = len(page_data['servers']) - page_data['server_stats']['running'] + page_data["servers"] = user_auth + page_data["server_stats"]["running"] = len( + list(filter(lambda x: x["stats"]["running"], page_data["servers"])) + ) + page_data["server_stats"]["stopped"] = ( + len(page_data["servers"]) - page_data["server_stats"]["running"] + ) - #set user server order - user_order = self.controller.users.get_user_by_id(exec_user['user_id']) - user_order = user_order['server_order'].split(',') + # set user server order + user_order = self.controller.users.get_user_by_id(exec_user["user_id"]) + user_order = user_order["server_order"].split(",") page_servers = [] server_ids = [] - un_used_servers = page_data['servers'] + un_used_servers = page_data["servers"] flag = 0 for server_id in user_order[:]: for server in un_used_servers[:]: if flag == 0: - server['stats']['downloading'] = self.controller.servers.get_download_status( - str(server['stats']['server_id']['server_id'])) - server['stats']['crashed'] = self.controller.servers.is_crashed( - str(server['stats']['server_id']['server_id'])) + server["stats"][ + "downloading" + ] = self.controller.servers.get_download_status( + str(server["stats"]["server_id"]["server_id"]) + ) + server["stats"]["crashed"] = self.controller.servers.is_crashed( + str(server["stats"]["server_id"]["server_id"]) + ) try: - server['stats']['waiting_start'] = self.controller.servers.get_waiting_start( - str(server['stats']['server_id']['server_id'])) + server["stats"][ + "waiting_start" + ] = self.controller.servers.get_waiting_start( + str(server["stats"]["server_id"]["server_id"]) + ) except Exception as e: logger.error(f"Failed to get server waiting to start: {e}") - server['stats']['waiting_start'] = False + server["stats"]["waiting_start"] = False - if str(server['server_data']['server_id']) == str(server_id): + if str(server["server_data"]["server_id"]) == str(server_id): page_servers.append(server) un_used_servers.remove(server) user_order.remove(server_id) - #we only want to set these server stats values once. We need to update the flag so it only hits that if once. + # we only want to set these server stats values once. We need to update the flag so it only hits that if once. flag += 1 - - for server in un_used_servers: - server_ids.append(str(server['server_data']['server_id'])) + server_ids.append(str(server["server_data"]["server_id"])) if server not in page_servers: page_servers.append(server) for server_id in user_order: - #remove IDs in list that user no longer has access to + # remove IDs in list that user no longer has access to if str(server_id) not in server_ids[:]: user_order.remove(server_id) - page_data['servers'] = page_servers + page_data["servers"] = page_servers - #num players is set to zero here. If we poll all servers while dashboard is loading it takes FOREVER. We leave this to the - #async polling once dashboard is served. - page_data['num_players'] = 0 + # num players is set to zero here. If we poll all servers while dashboard is loading it takes FOREVER. We leave this to the + # async polling once dashboard is served. + page_data["num_players"] = 0 template = "panel/dashboard.html" - elif page == 'server_detail': - subpage = bleach.clean(self.get_argument('subpage', "")) + elif page == "server_detail": + subpage = bleach.clean(self.get_argument("subpage", "")) server_id = self.check_server_id() if server_id is None: return - valid_subpages = ['term', 'logs', 'backup', 'config', 'files', 'admin_controls', 'schedules'] + valid_subpages = [ + "term", + "logs", + "backup", + "config", + "files", + "admin_controls", + "schedules", + ] server = self.controller.get_server_obj(server_id) # server_data isn't needed since the server_stats also pulls server data - page_data['server_data'] = self.controller.servers.get_server_data_by_id(server_id) - page_data['server_stats'] = self.controller.servers.get_server_stats_by_id(server_id) - page_data['downloading'] = self.controller.servers.get_download_status( - server_id) + page_data["server_data"] = self.controller.servers.get_server_data_by_id( + server_id + ) + page_data["server_stats"] = self.controller.servers.get_server_stats_by_id( + server_id + ) + page_data["downloading"] = self.controller.servers.get_download_status( + server_id + ) try: - page_data['waiting_start'] = self.controller.servers.get_waiting_start(server_id) + page_data["waiting_start"] = self.controller.servers.get_waiting_start( + server_id + ) except Exception as e: logger.error(f"Failed to get server waiting to start: {e}") - page_data['waiting_start'] = False - page_data['get_players'] = lambda: self.controller.stats.get_server_players(server_id) - page_data['active_link'] = subpage - page_data['permissions'] = { - 'Commands': Enum_Permissions_Server.Commands, - 'Terminal': Enum_Permissions_Server.Terminal, - 'Logs': Enum_Permissions_Server.Logs, - 'Schedule': Enum_Permissions_Server.Schedule, - 'Backup': Enum_Permissions_Server.Backup, - 'Files': Enum_Permissions_Server.Files, - 'Config': Enum_Permissions_Server.Config, - 'Players': Enum_Permissions_Server.Players, + page_data["waiting_start"] = False + page_data["get_players"] = lambda: self.controller.stats.get_server_players( + server_id + ) + page_data["active_link"] = subpage + page_data["permissions"] = { + "Commands": Enum_Permissions_Server.Commands, + "Terminal": Enum_Permissions_Server.Terminal, + "Logs": Enum_Permissions_Server.Logs, + "Schedule": Enum_Permissions_Server.Schedule, + "Backup": Enum_Permissions_Server.Backup, + "Files": Enum_Permissions_Server.Files, + "Config": Enum_Permissions_Server.Config, + "Players": Enum_Permissions_Server.Players, } - page_data['user_permissions'] = self.controller.server_perms.get_user_id_permissions_list(exec_user["user_id"], server_id) - page_data['server_stats']['crashed'] = self.controller.servers.is_crashed(server_id) - page_data['server_stats']['server_type'] = self.controller.servers.get_server_type_by_id(server_id) + page_data[ + "user_permissions" + ] = self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) + page_data["server_stats"]["crashed"] = self.controller.servers.is_crashed( + server_id + ) + page_data["server_stats"][ + "server_type" + ] = self.controller.servers.get_server_type_by_id(server_id) if subpage not in valid_subpages: - logger.debug('not a valid subpage') + logger.debug("not a valid subpage") if not subpage: - if page_data['permissions']['Terminal'] in page_data['user_permissions']: - subpage = 'term' - elif page_data['permissions']['Logs'] in page_data['user_permissions']: - subpage = 'logs' - elif page_data['permissions']['Schedule'] in page_data['user_permissions']: - subpage = 'schedules' - elif page_data['permissions']['Backup'] in page_data['user_permissions']: - subpage = 'backup' - elif page_data['permissions']['Files'] in page_data['user_permissions']: - subpage = 'files' - elif page_data['permissions']['Config'] in page_data['user_permissions']: - subpage = 'config' - elif page_data['permissions']['Players'] in page_data['user_permissions']: - subpage = 'admin_controls' + if ( + page_data["permissions"]["Terminal"] + in page_data["user_permissions"] + ): + subpage = "term" + elif page_data["permissions"]["Logs"] in page_data["user_permissions"]: + subpage = "logs" + elif ( + page_data["permissions"]["Schedule"] + in page_data["user_permissions"] + ): + subpage = "schedules" + elif ( + page_data["permissions"]["Backup"] in page_data["user_permissions"] + ): + subpage = "backup" + elif page_data["permissions"]["Files"] in page_data["user_permissions"]: + subpage = "files" + elif ( + page_data["permissions"]["Config"] in page_data["user_permissions"] + ): + subpage = "config" + elif ( + page_data["permissions"]["Players"] in page_data["user_permissions"] + ): + subpage = "admin_controls" else: self.redirect("/panel/error?error=Unauthorized access to Server") logger.debug(f'Subpage: "{subpage}"') - if subpage == 'term': - if not page_data['permissions']['Terminal'] in page_data['user_permissions']: + if subpage == "term": + if ( + not page_data["permissions"]["Terminal"] + in page_data["user_permissions"] + ): if not superuser: - self.redirect("/panel/error?error=Unauthorized access to Terminal") + self.redirect( + "/panel/error?error=Unauthorized access to Terminal" + ) return - if subpage == 'logs': - if not page_data['permissions']['Logs'] in page_data['user_permissions']: + if subpage == "logs": + if ( + not page_data["permissions"]["Logs"] + in page_data["user_permissions"] + ): if not superuser: self.redirect("/panel/error?error=Unauthorized access to Logs") return - - if subpage == 'schedules': - if not page_data['permissions']['Schedule'] in page_data['user_permissions']: + if subpage == "schedules": + if ( + not page_data["permissions"]["Schedule"] + in page_data["user_permissions"] + ): if not superuser: - self.redirect("/panel/error?error=Unauthorized access To Schedules") + self.redirect( + "/panel/error?error=Unauthorized access To Schedules" + ) return - page_data['schedules'] = management_helper.get_schedules_by_server(server_id) + page_data["schedules"] = management_helper.get_schedules_by_server( + server_id + ) - if subpage == 'config': - if not page_data['permissions']['Config'] in page_data['user_permissions']: + if subpage == "config": + if ( + not page_data["permissions"]["Config"] + in page_data["user_permissions"] + ): if not superuser: - self.redirect("/panel/error?error=Unauthorized access Server Config") + self.redirect( + "/panel/error?error=Unauthorized access Server Config" + ) return - if subpage == 'files': - if not page_data['permissions']['Files'] in page_data['user_permissions']: + if subpage == "files": + if ( + not page_data["permissions"]["Files"] + in page_data["user_permissions"] + ): if not superuser: self.redirect("/panel/error?error=Unauthorized access Files") return - if subpage == "backup": - if not page_data['permissions']['Backup'] in page_data['user_permissions']: + if ( + not page_data["permissions"]["Backup"] + in page_data["user_permissions"] + ): if not superuser: - self.redirect("/panel/error?error=Unauthorized access to Backups") + self.redirect( + "/panel/error?error=Unauthorized access to Backups" + ) return server_info = self.controller.servers.get_server_data_by_id(server_id) - page_data['backup_config'] = self.controller.management.get_backup_config(server_id) + page_data[ + "backup_config" + ] = self.controller.management.get_backup_config(server_id) exclusions = [] - page_data['exclusions'] = self.controller.management.get_excluded_backup_dirs(server_id) - page_data['backing_up'] = self.controller.get_server_obj(server_id).is_backingup - page_data['backup_stats'] = self.controller.get_server_obj(server_id).send_backup_status() - #makes it so relative path is the only thing shown - for file in page_data['exclusions']: + page_data[ + "exclusions" + ] = self.controller.management.get_excluded_backup_dirs(server_id) + page_data["backing_up"] = self.controller.get_server_obj( + server_id + ).is_backingup + page_data["backup_stats"] = self.controller.get_server_obj( + server_id + ).send_backup_status() + # makes it so relative path is the only thing shown + for file in page_data["exclusions"]: if helper.is_os_windows(): - exclusions.append(file.replace(server_info['path']+'\\', "")) + exclusions.append(file.replace(server_info["path"] + "\\", "")) else: - exclusions.append(file.replace(server_info['path']+'/', "")) - page_data['exclusions'] = exclusions + exclusions.append(file.replace(server_info["path"] + "/", "")) + page_data["exclusions"] = exclusions self.controller.refresh_server_settings(server_id) try: - page_data['backup_list'] = server.list_backups() + page_data["backup_list"] = server.list_backups() except: - page_data['backup_list'] = [] - page_data['backup_path'] = helper.wtol_path(server_info["backup_path"]) + page_data["backup_list"] = [] + page_data["backup_path"] = helper.wtol_path(server_info["backup_path"]) def get_banned_players_html(): banned_players = self.controller.servers.get_banned_players(server_id) @@ -515,26 +657,35 @@ class PanelHandler(BaseHandler): """ return html + if subpage == "admin_controls": - if not page_data['permissions']['Players'] in page_data['user_permissions']: + if ( + not page_data["permissions"]["Players"] + in page_data["user_permissions"] + ): if not superuser: self.redirect("/panel/error?error=Unauthorized access") - page_data['banned_players'] = get_banned_players_html() + page_data["banned_players"] = get_banned_players_html() template = f"panel/server_{subpage}.html" - elif page == 'download_backup': - file = self.get_argument('file', "") + elif page == "download_backup": + file = self.get_argument("file", "") server_id = self.check_server_id() if server_id is None: return - server_info = self.controller.servers.get_server_data_by_id(server_id) - backup_file = os.path.abspath(os.path.join(helper.get_os_understandable_path(server_info["backup_path"]), file)) - if not helper.in_path(helper.get_os_understandable_path(server_info["backup_path"]), backup_file) \ - or not os.path.isfile(backup_file): + backup_file = os.path.abspath( + os.path.join( + helper.get_os_understandable_path(server_info["backup_path"]), file + ) + ) + if not helper.in_path( + helper.get_os_understandable_path(server_info["backup_path"]), + backup_file, + ) or not os.path.isfile(backup_file): self.redirect("/panel/error?error=Invalid path detected") return @@ -542,18 +693,22 @@ class PanelHandler(BaseHandler): self.redirect(f"/panel/server_detail?id={server_id}&subpage=backup") - elif page == 'panel_config': + elif page == "panel_config": auth_servers = {} auth_role_servers = {} roles = self.controller.roles.get_all_roles() user_roles = {} for user in self.controller.users.get_all_users(): - user_roles_list = self.controller.users.get_user_roles_names(user.user_id) - user_servers = self.controller.servers.get_authorized_servers(user.user_id) + user_roles_list = self.controller.users.get_user_roles_names( + user.user_id + ) + user_servers = self.controller.servers.get_authorized_servers( + user.user_id + ) servers = [] for server in user_servers: - if server['server_name'] not in servers: - servers.append(server['server_name']) + if server["server_name"] not in servers: + servers.append(server["server_name"]) new_item = {user.user_id: servers} auth_servers.update(new_item) data = {user.user_id: user_roles_list} @@ -561,108 +716,138 @@ class PanelHandler(BaseHandler): for role in roles: role_servers = [] role = self.controller.roles.get_role_with_servers(role.role_id) - for serv_id in role['servers']: - role_servers.append(self.controller.servers.get_server_data_by_id(serv_id)['server_name']) - data = {role['role_id']: role_servers} + for serv_id in role["servers"]: + role_servers.append( + self.controller.servers.get_server_data_by_id(serv_id)[ + "server_name" + ] + ) + data = {role["role_id"]: role_servers} auth_role_servers.update(data) + page_data["auth-servers"] = auth_servers + page_data["role-servers"] = auth_role_servers + page_data["user-roles"] = user_roles - page_data['auth-servers'] = auth_servers - page_data['role-servers'] = auth_role_servers - page_data['user-roles'] = user_roles + page_data["users"] = self.controller.users.user_query(exec_user["user_id"]) + page_data["roles"] = self.controller.users.user_role_query( + exec_user["user_id"] + ) - page_data['users'] = self.controller.users.user_query(exec_user['user_id']) - page_data['roles'] = self.controller.users.user_role_query(exec_user['user_id']) - - - for user in page_data['users']: - if user.user_id != exec_user['user_id']: + for user in page_data["users"]: + if user.user_id != exec_user["user_id"]: user.api_token = "********" if superuser: for user in self.controller.users.get_all_users(): if user.superuser: super_auth_servers = ["Super User Access To All Servers"] - page_data['users'] = self.controller.users.get_all_users() - page_data['roles'] = self.controller.roles.get_all_roles() - page_data['auth-servers'][user.user_id] = super_auth_servers + page_data["users"] = self.controller.users.get_all_users() + page_data["roles"] = self.controller.roles.get_all_roles() + page_data["auth-servers"][user.user_id] = super_auth_servers template = "panel/panel_config.html" elif page == "add_user": - page_data['new_user'] = True - page_data['user'] = {} - page_data['user']['username'] = "" - page_data['user']['user_id'] = -1 - page_data['user']['email'] = "" - page_data['user']['enabled'] = True - page_data['user']['superuser'] = False - page_data['user']['created'] = "N/A" - page_data['user']['last_login'] = "N/A" - page_data['user']['last_ip'] = "N/A" - page_data['user']['last_update'] = "N/A" - page_data['user']['roles'] = set() + page_data["new_user"] = True + page_data["user"] = {} + page_data["user"]["username"] = "" + page_data["user"]["user_id"] = -1 + page_data["user"]["email"] = "" + page_data["user"]["enabled"] = True + page_data["user"]["superuser"] = False + page_data["user"]["created"] = "N/A" + page_data["user"]["last_login"] = "N/A" + page_data["user"]["last_ip"] = "N/A" + page_data["user"]["last_update"] = "N/A" + page_data["user"]["roles"] = set() if Enum_Permissions_Crafty.User_Config not in exec_user_crafty_permissions: - self.redirect("/panel/error?error=Unauthorized access: not a user editor") + self.redirect( + "/panel/error?error=Unauthorized access: not a user editor" + ) return - page_data['roles_all'] = self.controller.roles.get_all_roles() - page_data['servers'] = [] - page_data['servers_all'] = self.controller.list_defined_servers() - page_data['role-servers'] = [] - page_data['permissions_all'] = self.controller.crafty_perms.list_defined_crafty_permissions() - page_data['permissions_list'] = set() - page_data['quantity_server'] = self.controller.crafty_perms.list_all_crafty_permissions_quantity_limits() - page_data['languages'] = [] - page_data['languages'].append(self.controller.users.get_user_lang_by_id(exec_user["user_id"])) + page_data["roles_all"] = self.controller.roles.get_all_roles() + page_data["servers"] = [] + page_data["servers_all"] = self.controller.list_defined_servers() + page_data["role-servers"] = [] + page_data[ + "permissions_all" + ] = self.controller.crafty_perms.list_defined_crafty_permissions() + page_data["permissions_list"] = set() + page_data[ + "quantity_server" + ] = ( + self.controller.crafty_perms.list_all_crafty_permissions_quantity_limits() + ) + page_data["languages"] = [] + page_data["languages"].append( + self.controller.users.get_user_lang_by_id(exec_user["user_id"]) + ) if superuser: - page_data['super-disabled'] = '' + page_data["super-disabled"] = "" else: - page_data['super-disabled'] = 'disabled' - for file in sorted(os.listdir(os.path.join(helper.root_dir, 'app', 'translations'))): - if file.endswith('.json'): - if file not in helper.get_setting('disabled_language_files'): - if file != str(page_data['languages'][0] + '.json'): - page_data['languages'].append(file.split('.')[0]) + page_data["super-disabled"] = "disabled" + for file in sorted( + os.listdir(os.path.join(helper.root_dir, "app", "translations")) + ): + if file.endswith(".json"): + if file not in helper.get_setting("disabled_language_files"): + if file != str(page_data["languages"][0] + ".json"): + page_data["languages"].append(file.split(".")[0]) template = "panel/panel_edit_user.html" elif page == "add_schedule": - server_id = self.get_argument('id', None) - page_data['schedules'] = management_helper.get_schedules_by_server(server_id) - page_data['get_players'] = lambda: self.controller.stats.get_server_players(server_id) - page_data['active_link'] = 'schedules' - page_data['permissions'] = { - 'Commands': Enum_Permissions_Server.Commands, - 'Terminal': Enum_Permissions_Server.Terminal, - 'Logs': Enum_Permissions_Server.Logs, - 'Schedule': Enum_Permissions_Server.Schedule, - 'Backup': Enum_Permissions_Server.Backup, - 'Files': Enum_Permissions_Server.Files, - 'Config': Enum_Permissions_Server.Config, - 'Players': Enum_Permissions_Server.Players, + server_id = self.get_argument("id", None) + page_data["schedules"] = management_helper.get_schedules_by_server( + server_id + ) + page_data["get_players"] = lambda: self.controller.stats.get_server_players( + server_id + ) + page_data["active_link"] = "schedules" + page_data["permissions"] = { + "Commands": Enum_Permissions_Server.Commands, + "Terminal": Enum_Permissions_Server.Terminal, + "Logs": Enum_Permissions_Server.Logs, + "Schedule": Enum_Permissions_Server.Schedule, + "Backup": Enum_Permissions_Server.Backup, + "Files": Enum_Permissions_Server.Files, + "Config": Enum_Permissions_Server.Config, + "Players": Enum_Permissions_Server.Players, } - page_data['user_permissions'] = self.controller.server_perms.get_user_id_permissions_list(exec_user["user_id"], server_id) - page_data['server_data'] = self.controller.servers.get_server_data_by_id(server_id) - page_data['server_stats'] = self.controller.servers.get_server_stats_by_id(server_id) - page_data['server_stats']['server_type'] = self.controller.servers.get_server_type_by_id(server_id) - page_data['new_schedule'] = True - page_data['schedule'] = {} - page_data['schedule']['children'] = [] - page_data['schedule']['server_id'] = server_id - page_data['schedule']['schedule_id'] = '' - page_data['schedule']['action'] = "" - page_data['schedule']['enabled'] = True - page_data['schedule']['command'] = '' - page_data['schedule']['one_time'] = False - page_data['schedule']['cron_string'] = "" - page_data['schedule']['time'] = "" - page_data['schedule']['interval'] = "" - #we don't need to check difficulty here. We'll just default to basic for new schedules - page_data['schedule']['difficulty'] = "basic" - page_data['schedule']['interval_type'] = 'days' + page_data[ + "user_permissions" + ] = self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) + page_data["server_data"] = self.controller.servers.get_server_data_by_id( + server_id + ) + page_data["server_stats"] = self.controller.servers.get_server_stats_by_id( + server_id + ) + page_data["server_stats"][ + "server_type" + ] = self.controller.servers.get_server_type_by_id(server_id) + page_data["new_schedule"] = True + page_data["schedule"] = {} + page_data["schedule"]["children"] = [] + page_data["schedule"]["server_id"] = server_id + page_data["schedule"]["schedule_id"] = "" + page_data["schedule"]["action"] = "" + page_data["schedule"]["enabled"] = True + page_data["schedule"]["command"] = "" + page_data["schedule"]["one_time"] = False + page_data["schedule"]["cron_string"] = "" + page_data["schedule"]["time"] = "" + page_data["schedule"]["interval"] = "" + # we don't need to check difficulty here. We'll just default to basic for new schedules + page_data["schedule"]["difficulty"] = "basic" + page_data["schedule"]["interval_type"] = "days" - if not Enum_Permissions_Server.Schedule in page_data['user_permissions']: + if not Enum_Permissions_Server.Schedule in page_data["user_permissions"]: if not superuser: self.redirect("/panel/error?error=Unauthorized access To Schedules") return @@ -670,56 +855,77 @@ class PanelHandler(BaseHandler): template = "panel/server_schedule_edit.html" elif page == "edit_schedule": - server_id = self.get_argument('id', None) - page_data['schedules'] = management_helper.get_schedules_by_server(server_id) - sch_id = self.get_argument('sch_id', None) + server_id = self.get_argument("id", None) + page_data["schedules"] = management_helper.get_schedules_by_server( + server_id + ) + sch_id = self.get_argument("sch_id", None) schedule = self.controller.management.get_scheduled_task_model(sch_id) - page_data['get_players'] = lambda: self.controller.stats.get_server_players(server_id) - page_data['active_link'] = 'schedules' - page_data['permissions'] = { - 'Commands': Enum_Permissions_Server.Commands, - 'Terminal': Enum_Permissions_Server.Terminal, - 'Logs': Enum_Permissions_Server.Logs, - 'Schedule': Enum_Permissions_Server.Schedule, - 'Backup': Enum_Permissions_Server.Backup, - 'Files': Enum_Permissions_Server.Files, - 'Config': Enum_Permissions_Server.Config, - 'Players': Enum_Permissions_Server.Players, + page_data["get_players"] = lambda: self.controller.stats.get_server_players( + server_id + ) + page_data["active_link"] = "schedules" + page_data["permissions"] = { + "Commands": Enum_Permissions_Server.Commands, + "Terminal": Enum_Permissions_Server.Terminal, + "Logs": Enum_Permissions_Server.Logs, + "Schedule": Enum_Permissions_Server.Schedule, + "Backup": Enum_Permissions_Server.Backup, + "Files": Enum_Permissions_Server.Files, + "Config": Enum_Permissions_Server.Config, + "Players": Enum_Permissions_Server.Players, } - page_data['user_permissions'] = self.controller.server_perms.get_user_id_permissions_list(exec_user["user_id"], server_id) - page_data['server_data'] = self.controller.servers.get_server_data_by_id(server_id) - page_data['server_stats'] = self.controller.servers.get_server_stats_by_id(server_id) - page_data['server_stats']['server_type'] = self.controller.servers.get_server_type_by_id(server_id) - page_data['new_schedule'] = False - page_data['schedule'] = {} - page_data['schedule']['server_id'] = server_id - page_data['schedule']['schedule_id'] = schedule.schedule_id - page_data['schedule']['action'] = schedule.action - page_data['schedule']['children'] = self.controller.management.get_child_schedules(sch_id) + page_data[ + "user_permissions" + ] = self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) + page_data["server_data"] = self.controller.servers.get_server_data_by_id( + server_id + ) + page_data["server_stats"] = self.controller.servers.get_server_stats_by_id( + server_id + ) + page_data["server_stats"][ + "server_type" + ] = self.controller.servers.get_server_type_by_id(server_id) + page_data["new_schedule"] = False + page_data["schedule"] = {} + page_data["schedule"]["server_id"] = server_id + page_data["schedule"]["schedule_id"] = schedule.schedule_id + page_data["schedule"]["action"] = schedule.action + page_data["schedule"][ + "children" + ] = self.controller.management.get_child_schedules(sch_id) # We check here to see if the command is any of the default ones. # We do not want a user changing to a custom command and seeing our command there. - if schedule.action != 'start' or schedule.action != 'stop' or schedule.action != 'restart' or schedule.action != 'backup': - page_data['schedule']['command'] = schedule.command + if ( + schedule.action != "start" + or schedule.action != "stop" + or schedule.action != "restart" + or schedule.action != "backup" + ): + page_data["schedule"]["command"] = schedule.command else: - page_data['schedule']['command'] = '' - page_data['schedule']['enabled'] = schedule.enabled - page_data['schedule']['one_time'] = schedule.one_time - page_data['schedule']['cron_string'] = schedule.cron_string - page_data['schedule']['time'] = schedule.start_time - page_data['schedule']['interval'] = schedule.interval - page_data['schedule']['interval_type'] = schedule.interval_type - if schedule.interval_type == 'reaction': - difficulty = 'reaction' - elif schedule.cron_string == '': - difficulty = 'basic' + page_data["schedule"]["command"] = "" + page_data["schedule"]["enabled"] = schedule.enabled + page_data["schedule"]["one_time"] = schedule.one_time + page_data["schedule"]["cron_string"] = schedule.cron_string + page_data["schedule"]["time"] = schedule.start_time + page_data["schedule"]["interval"] = schedule.interval + page_data["schedule"]["interval_type"] = schedule.interval_type + if schedule.interval_type == "reaction": + difficulty = "reaction" + elif schedule.cron_string == "": + difficulty = "basic" else: - difficulty = 'advanced' - page_data['schedule']['difficulty'] = difficulty + difficulty = "advanced" + page_data["schedule"]["difficulty"] = difficulty if sch_id is None or server_id is None: self.redirect("/panel/error?error=Invalid server ID or Schedule ID") - if not Enum_Permissions_Server.Schedule in page_data['user_permissions']: + if not Enum_Permissions_Server.Schedule in page_data["user_permissions"]: if not superuser: self.redirect("/panel/error?error=Unauthorized access To Schedules") return @@ -727,62 +933,82 @@ class PanelHandler(BaseHandler): template = "panel/server_schedule_edit.html" elif page == "edit_user": - user_id = self.get_argument('id', None) + user_id = self.get_argument("id", None) role_servers = self.controller.servers.get_authorized_servers(user_id) page_role_servers = [] servers = set() for server in role_servers: - page_role_servers.append(server['server_id']) - page_data['new_user'] = False - page_data['user'] = self.controller.users.get_user_by_id(user_id) - page_data['servers'] = servers - page_data['role-servers'] = page_role_servers - page_data['roles_all'] = self.controller.roles.get_all_roles() - page_data['servers_all'] = self.controller.list_defined_servers() - page_data['permissions_all'] = self.controller.crafty_perms.list_defined_crafty_permissions() - page_data['permissions_list'] = self.controller.crafty_perms.get_crafty_permissions_list(user_id) - page_data['quantity_server'] = self.controller.crafty_perms.list_crafty_permissions_quantity_limits(user_id) - page_data['languages'] = [] - page_data['languages'].append(self.controller.users.get_user_lang_by_id(user_id)) - #checks if super user. If not we disable the button. - if superuser and str(exec_user['user_id']) != str(user_id): - page_data['super-disabled'] = '' + page_role_servers.append(server["server_id"]) + page_data["new_user"] = False + page_data["user"] = self.controller.users.get_user_by_id(user_id) + page_data["servers"] = servers + page_data["role-servers"] = page_role_servers + page_data["roles_all"] = self.controller.roles.get_all_roles() + page_data["servers_all"] = self.controller.list_defined_servers() + page_data[ + "permissions_all" + ] = self.controller.crafty_perms.list_defined_crafty_permissions() + page_data[ + "permissions_list" + ] = self.controller.crafty_perms.get_crafty_permissions_list(user_id) + page_data[ + "quantity_server" + ] = self.controller.crafty_perms.list_crafty_permissions_quantity_limits( + user_id + ) + page_data["languages"] = [] + page_data["languages"].append( + self.controller.users.get_user_lang_by_id(user_id) + ) + # checks if super user. If not we disable the button. + if superuser and str(exec_user["user_id"]) != str(user_id): + page_data["super-disabled"] = "" else: - page_data['super-disabled'] = 'disabled' + page_data["super-disabled"] = "disabled" - for file in sorted(os.listdir(os.path.join(helper.root_dir, 'app', 'translations'))): - if file.endswith('.json'): - if file not in helper.get_setting('disabled_language_files'): - if file != str(page_data['languages'][0] + '.json'): - page_data['languages'].append(file.split('.')[0]) + for file in sorted( + os.listdir(os.path.join(helper.root_dir, "app", "translations")) + ): + if file.endswith(".json"): + if file not in helper.get_setting("disabled_language_files"): + if file != str(page_data["languages"][0] + ".json"): + page_data["languages"].append(file.split(".")[0]) if user_id is None: self.redirect("/panel/error?error=Invalid User ID") return - elif Enum_Permissions_Crafty.User_Config not in exec_user_crafty_permissions: + elif ( + Enum_Permissions_Crafty.User_Config not in exec_user_crafty_permissions + ): if str(user_id) != str(exec_user["user_id"]): - self.redirect("/panel/error?error=Unauthorized access: not a user editor") + self.redirect( + "/panel/error?error=Unauthorized access: not a user editor" + ) return - page_data['servers'] = [] - page_data['role-servers'] = [] - page_data['roles_all'] = [] - page_data['servers_all'] = [] + page_data["servers"] = [] + page_data["role-servers"] = [] + page_data["roles_all"] = [] + page_data["servers_all"] = [] - if exec_user['user_id'] != page_data['user']['user_id']: - page_data['user']['api_token'] = "********" + if exec_user["user_id"] != page_data["user"]["user_id"]: + page_data["user"]["api_token"] = "********" - if exec_user['email'] == 'default@example.com': - page_data['user']['email'] = "" + if exec_user["email"] == "default@example.com": + page_data["user"]["email"] = "" template = "panel/panel_edit_user.html" elif page == "edit_user_apikeys": - user_id = self.get_argument('id', None) - page_data['user'] = self.controller.users.get_user_by_id(user_id) - page_data['api_keys'] = self.controller.users.get_user_api_keys(user_id) + user_id = self.get_argument("id", None) + page_data["user"] = self.controller.users.get_user_by_id(user_id) + page_data["api_keys"] = self.controller.users.get_user_api_keys(user_id) # self.controller.crafty_perms.list_defined_crafty_permissions() - page_data['server_permissions_all'] = self.controller.server_perms.list_defined_permissions() - page_data['crafty_permissions_all'] = self.controller.crafty_perms.list_defined_crafty_permissions() + page_data[ + "server_permissions_all" + ] = self.controller.server_perms.list_defined_permissions() + page_data[ + "crafty_permissions_all" + ] = self.controller.crafty_perms.list_defined_crafty_permissions() if user_id is None: self.redirect("/panel/error?error=Invalid User ID") @@ -791,14 +1017,20 @@ class PanelHandler(BaseHandler): template = "panel/panel_edit_user_apikeys.html" elif page == "remove_user": - user_id = bleach.clean(self.get_argument('id', None)) + user_id = bleach.clean(self.get_argument("id", None)) - if not superuser and Enum_Permissions_Crafty.User_Config not in exec_user_crafty_permissions: + if ( + not superuser + and Enum_Permissions_Crafty.User_Config + not in exec_user_crafty_permissions + ): self.redirect("/panel/error?error=Unauthorized access: not superuser") return elif str(exec_user["user_id"]) == str(user_id): - self.redirect("/panel/error?error=Unauthorized access: you cannot delete yourself") + self.redirect( + "/panel/error?error=Unauthorized access: you cannot delete yourself" + ) return elif user_id is None: self.redirect("/panel/error?error=Invalid User ID") @@ -809,52 +1041,64 @@ class PanelHandler(BaseHandler): if not target_user: self.redirect("/panel/error?error=Invalid User ID") return - elif target_user['superuser']: + elif target_user["superuser"]: self.redirect("/panel/error?error=Cannot remove a superuser") return self.controller.users.remove_user(user_id) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Removed user {target_user['username']} (UID:{user_id})", - server_id=0, - source_ip=self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Removed user {target_user['username']} (UID:{user_id})", + server_id=0, + source_ip=self.get_remote_ip(), + ) self.redirect("/panel/panel_config") elif page == "add_role": user_roles = self.get_user_roles() - page_data['new_role'] = True - page_data['role'] = {} - page_data['role']['role_name'] = "" - page_data['role']['role_id'] = -1 - page_data['role']['created'] = "N/A" - page_data['role']['last_update'] = "N/A" - page_data['role']['servers'] = set() - page_data['user-roles'] = user_roles - page_data['users'] = self.controller.users.get_all_users() + page_data["new_role"] = True + page_data["role"] = {} + page_data["role"]["role_name"] = "" + page_data["role"]["role_id"] = -1 + page_data["role"]["created"] = "N/A" + page_data["role"]["last_update"] = "N/A" + page_data["role"]["servers"] = set() + page_data["user-roles"] = user_roles + page_data["users"] = self.controller.users.get_all_users() if Enum_Permissions_Crafty.Roles_Config not in exec_user_crafty_permissions: - self.redirect("/panel/error?error=Unauthorized access: not a role editor") + self.redirect( + "/panel/error?error=Unauthorized access: not a role editor" + ) return - page_data['servers_all'] = self.controller.list_defined_servers() - page_data['permissions_all'] = self.controller.server_perms.list_defined_permissions() - page_data['permissions_list'] = set() + page_data["servers_all"] = self.controller.list_defined_servers() + page_data[ + "permissions_all" + ] = self.controller.server_perms.list_defined_permissions() + page_data["permissions_list"] = set() template = "panel/panel_edit_role.html" elif page == "edit_role": user_roles = self.get_user_roles() - page_data['new_role'] = False - role_id = self.get_argument('id', None) - page_data['role'] = self.controller.roles.get_role_with_servers(role_id) - page_data['servers_all'] = self.controller.list_defined_servers() - page_data['permissions_all'] = self.controller.server_perms.list_defined_permissions() - page_data['permissions_list'] = self.controller.server_perms.get_role_permissions(role_id) - page_data['user-roles'] = user_roles - page_data['users'] = self.controller.users.get_all_users() + page_data["new_role"] = False + role_id = self.get_argument("id", None) + page_data["role"] = self.controller.roles.get_role_with_servers(role_id) + page_data["servers_all"] = self.controller.list_defined_servers() + page_data[ + "permissions_all" + ] = self.controller.server_perms.list_defined_permissions() + page_data[ + "permissions_list" + ] = self.controller.server_perms.get_role_permissions(role_id) + page_data["user-roles"] = user_roles + page_data["users"] = self.controller.users.get_all_users() if Enum_Permissions_Crafty.Roles_Config not in exec_user_crafty_permissions: - self.redirect("/panel/error?error=Unauthorized access: not a role editor") + self.redirect( + "/panel/error?error=Unauthorized access: not a role editor" + ) return elif role_id is None: self.redirect("/panel/error?error=Invalid Role ID") @@ -863,7 +1107,7 @@ class PanelHandler(BaseHandler): template = "panel/panel_edit_role.html" elif page == "remove_role": - role_id = bleach.clean(self.get_argument('id', None)) + role_id = bleach.clean(self.get_argument("id", None)) if not superuser: self.redirect("/panel/error?error=Unauthorized access: not superuser") @@ -880,20 +1124,22 @@ class PanelHandler(BaseHandler): self.controller.roles.remove_role(role_id) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Removed role {target_role['role_name']} (RID:{role_id})", - server_id=0, - source_ip=self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Removed role {target_role['role_name']} (RID:{role_id})", + server_id=0, + source_ip=self.get_remote_ip(), + ) self.redirect("/panel/panel_config") elif page == "activity_logs": - page_data['audit_logs'] = self.controller.management.get_actity_log() + page_data["audit_logs"] = self.controller.management.get_actity_log() template = "panel/activity_logs.html" - elif page == 'download_file': - file = helper.get_os_understandable_path(self.get_argument('path', "")) - name = self.get_argument('name', "") + elif page == "download_file": + file = helper.get_os_understandable_path(self.get_argument("path", "")) + name = self.get_argument("name", "") server_id = self.check_server_id() if server_id is None: @@ -901,31 +1147,34 @@ class PanelHandler(BaseHandler): server_info = self.controller.servers.get_server_data_by_id(server_id) - if not helper.in_path(helper.get_os_understandable_path(server_info["path"]), file) \ - or not os.path.isfile(file): + if not helper.in_path( + helper.get_os_understandable_path(server_info["path"]), file + ) or not os.path.isfile(file): self.redirect("/panel/error?error=Invalid path detected") return self.download_file(name, file) self.redirect(f"/panel/server_detail?id={server_id}&subpage=files") - elif page == 'download_support_package': - tempZipStorage = exec_user['support_logs'] - #We'll reset the support path for this user now. + elif page == "download_support_package": + tempZipStorage = exec_user["support_logs"] + # We'll reset the support path for this user now. self.controller.users.set_support_path(exec_user["user_id"], "") - self.set_header('Content-Type', 'application/octet-stream') - self.set_header('Content-Disposition', 'attachment; filename=' + "support_logs.zip") - chunk_size = 1024 * 1024 * 4 # 4 MiB - if tempZipStorage != '': - with open(tempZipStorage, 'rb') as f: + self.set_header("Content-Type", "application/octet-stream") + self.set_header( + "Content-Disposition", "attachment; filename=" + "support_logs.zip" + ) + chunk_size = 1024 * 1024 * 4 # 4 MiB + if tempZipStorage != "": + with open(tempZipStorage, "rb") as f: while True: chunk = f.read(chunk_size) if not chunk: break try: - self.write(chunk) # write the chunk to response - self.flush() # send the chunk to client + self.write(chunk) # write the chunk to response + self.flush() # send the chunk to client except iostream.StreamClosedError: # this means the client has closed the connection # so break the loop @@ -936,23 +1185,25 @@ class PanelHandler(BaseHandler): # same time, the chunks in memory will keep # increasing and will eat up the RAM del chunk - self.redirect('/panel/dashboard') + self.redirect("/panel/dashboard") else: - self.redirect('/panel/error?error=No path found for support logs') + self.redirect("/panel/error?error=No path found for support logs") return elif page == "support_logs": - logger.info(f"Support logs requested. Packinging logs for user with ID: {exec_user['user_id']}") - logs_thread = threading.Thread(target=self.controller.package_support_logs, - daemon=True, - args=(exec_user,), - name=f"{exec_user['user_id']}_logs_thread") + logger.info( + f"Support logs requested. Packinging logs for user with ID: {exec_user['user_id']}" + ) + logs_thread = threading.Thread( + target=self.controller.package_support_logs, + daemon=True, + args=(exec_user,), + name=f"{exec_user['user_id']}_logs_thread", + ) logs_thread.start() - self.redirect('/panel/dashboard') + self.redirect("/panel/dashboard") return - - self.render( template, data=page_data, @@ -964,62 +1215,72 @@ class PanelHandler(BaseHandler): @tornado.web.authenticated def post(self, page): api_key, _token_data, exec_user = self.current_user - superuser = exec_user['superuser'] + superuser = exec_user["superuser"] if api_key is not None: superuser = superuser and api_key.superuser - server_id = self.get_argument('id', None) + server_id = self.get_argument("id", None) permissions = { - 'Commands': Enum_Permissions_Server.Commands, - 'Terminal': Enum_Permissions_Server.Terminal, - 'Logs': Enum_Permissions_Server.Logs, - 'Schedule': Enum_Permissions_Server.Schedule, - 'Backup': Enum_Permissions_Server.Backup, - 'Files': Enum_Permissions_Server.Files, - 'Config': Enum_Permissions_Server.Config, - 'Players': Enum_Permissions_Server.Players, - } + "Commands": Enum_Permissions_Server.Commands, + "Terminal": Enum_Permissions_Server.Terminal, + "Logs": Enum_Permissions_Server.Logs, + "Schedule": Enum_Permissions_Server.Schedule, + "Backup": Enum_Permissions_Server.Backup, + "Files": Enum_Permissions_Server.Files, + "Config": Enum_Permissions_Server.Config, + "Players": Enum_Permissions_Server.Players, + } exec_user_role = set() if superuser: # defined_servers = self.controller.list_defined_servers() exec_user_role.add("Super User") - exec_user_crafty_permissions = self.controller.crafty_perms.list_defined_crafty_permissions() + exec_user_crafty_permissions = ( + self.controller.crafty_perms.list_defined_crafty_permissions() + ) else: - exec_user_crafty_permissions = self.controller.crafty_perms.get_crafty_permissions_list(exec_user["user_id"]) + exec_user_crafty_permissions = ( + self.controller.crafty_perms.get_crafty_permissions_list( + exec_user["user_id"] + ) + ) # defined_servers = self.controller.servers.get_authorized_servers(exec_user["user_id"]) - for r in exec_user['roles']: + for r in exec_user["roles"]: role = self.controller.roles.get_role(r) - exec_user_role.add(role['role_name']) + exec_user_role.add(role["role_name"]) - if page == 'server_detail': - if not permissions['Config'] in self.controller.server_perms.get_user_id_permissions_list(exec_user["user_id"], server_id): + if page == "server_detail": + if not permissions[ + "Config" + ] in self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ): if not superuser: self.redirect("/panel/error?error=Unauthorized access to Config") return - server_name = self.get_argument('server_name', None) + server_name = self.get_argument("server_name", None) server_obj = self.controller.servers.get_server_obj(server_id) if superuser: - server_path = self.get_argument('server_path', None) + server_path = self.get_argument("server_path", None) if helper.is_os_windows(): - server_path.replace(' ', '^ ') + server_path.replace(" ", "^ ") server_path = helper.wtol_path(server_path) - log_path = self.get_argument('log_path', None) + log_path = self.get_argument("log_path", None) if helper.is_os_windows(): - log_path.replace(' ', '^ ') + log_path.replace(" ", "^ ") log_path = helper.wtol_path(log_path) - executable = self.get_argument('executable', None) - execution_command = self.get_argument('execution_command', None) - server_ip = self.get_argument('server_ip', None) - server_port = self.get_argument('server_port', None) - executable_update_url = self.get_argument('executable_update_url', None) + executable = self.get_argument("executable", None) + execution_command = self.get_argument("execution_command", None) + server_ip = self.get_argument("server_ip", None) + server_port = self.get_argument("server_port", None) + executable_update_url = self.get_argument("executable_update_url", None) else: execution_command = server_obj.execution_command executable = server_obj.executable - stop_command = self.get_argument('stop_command', None) - auto_start_delay = self.get_argument('auto_start_delay', '10') - auto_start = int(float(self.get_argument('auto_start', '0'))) - crash_detection = int(float(self.get_argument('crash_detection', '0'))) - logs_delete_after = int(float(self.get_argument('logs_delete_after', '0'))) + stop_command = self.get_argument("stop_command", None) + auto_start_delay = self.get_argument("auto_start_delay", "10") + auto_start = int(float(self.get_argument("auto_start", "0"))) + crash_detection = int(float(self.get_argument("crash_detection", "0"))) + logs_delete_after = int(float(self.get_argument("logs_delete_after", "0"))) # subpage = self.get_argument('subpage', None) server_id = self.check_server_id() @@ -1028,13 +1289,17 @@ class PanelHandler(BaseHandler): server_obj = self.controller.servers.get_server_obj(server_id) stale_executable = server_obj.executable - #Compares old jar name to page data being passed. If they are different we replace the executable name in the + # Compares old jar name to page data being passed. If they are different we replace the executable name in the if str(stale_executable) != str(executable): - execution_command = execution_command.replace(str(stale_executable), str(executable)) + execution_command = execution_command.replace( + str(stale_executable), str(executable) + ) server_obj.server_name = server_name if superuser: - if helper.validate_traversal(helper.get_servers_root_dir(), server_path): + if helper.validate_traversal( + helper.get_servers_root_dir(), server_path + ): server_obj.path = server_path server_obj.log_path = log_path if helper.validate_traversal(helper.get_servers_root_dir(), executable): @@ -1061,35 +1326,43 @@ class PanelHandler(BaseHandler): self.controller.refresh_server_settings(server_id) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Edited server {server_id} named {server_name}", - server_id, - self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Edited server {server_id} named {server_name}", + server_id, + self.get_remote_ip(), + ) self.redirect(f"/panel/server_detail?id={server_id}&subpage=config") if page == "server_backup": logger.debug(self.request.arguments) - server_id = self.get_argument('id', None) + server_id = self.get_argument("id", None) server_obj = self.controller.servers.get_server_obj(server_id) - compress = self.get_argument('compress', False) - check_changed = self.get_argument('changed') + compress = self.get_argument("compress", False) + check_changed = self.get_argument("changed") if str(check_changed) == str(1): - checked = self.get_body_arguments('root_path') + checked = self.get_body_arguments("root_path") else: checked = self.controller.management.get_excluded_backup_dirs(server_id) if superuser: - backup_path = bleach.clean(self.get_argument('backup_path', None)) + backup_path = bleach.clean(self.get_argument("backup_path", None)) if helper.is_os_windows(): - backup_path.replace(' ', '^ ') + backup_path.replace(" ", "^ ") backup_path = helper.wtol_path(backup_path) else: backup_path = server_obj.backup_path - max_backups = bleach.clean(self.get_argument('max_backups', None)) + max_backups = bleach.clean(self.get_argument("max_backups", None)) - if not permissions['Backup'] in self.controller.server_perms.get_user_id_permissions_list(exec_user["user_id"], server_id): + if not permissions[ + "Backup" + ] in self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ): if not superuser: - self.redirect("/panel/error?error=Unauthorized access: User not authorized") + self.redirect( + "/panel/error?error=Unauthorized access: User not authorized" + ) return elif server_id is None: self.redirect("/panel/error?error=Invalid Server ID") @@ -1103,30 +1376,36 @@ class PanelHandler(BaseHandler): server_obj = self.controller.servers.get_server_obj(server_id) server_obj.backup_path = backup_path self.controller.servers.update_server(server_obj) - self.controller.management.set_backup_config(server_id, max_backups=max_backups, excluded_dirs=checked, compress=bool(compress)) + self.controller.management.set_backup_config( + server_id, + max_backups=max_backups, + excluded_dirs=checked, + compress=bool(compress), + ) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Edited server {server_id}: updated backups", - server_id, - self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Edited server {server_id}: updated backups", + server_id, + self.get_remote_ip(), + ) self.tasks_manager.reload_schedule_from_db() self.redirect(f"/panel/server_detail?id={server_id}&subpage=backup") - if page == "new_schedule": - server_id = bleach.clean(self.get_argument('id', None)) - difficulty = bleach.clean(self.get_argument('difficulty', None)) + server_id = bleach.clean(self.get_argument("id", None)) + difficulty = bleach.clean(self.get_argument("difficulty", None)) server_obj = self.controller.servers.get_server_obj(server_id) - enabled = bleach.clean(self.get_argument('enabled', '0')) - if difficulty == 'basic': - action = bleach.clean(self.get_argument('action', None)) - interval = bleach.clean(self.get_argument('interval', None)) - interval_type = bleach.clean(self.get_argument('interval_type', None)) - #only check for time if it's number of days + enabled = bleach.clean(self.get_argument("enabled", "0")) + if difficulty == "basic": + action = bleach.clean(self.get_argument("action", None)) + interval = bleach.clean(self.get_argument("interval", None)) + interval_type = bleach.clean(self.get_argument("interval_type", None)) + # only check for time if it's number of days if interval_type == "days": - sch_time = bleach.clean(self.get_argument('time', None)) + sch_time = bleach.clean(self.get_argument("time", None)) if action == "command": - command = bleach.clean(self.get_argument('command', None)) + command = bleach.clean(self.get_argument("command", None)) elif action == "start": command = "start_server" elif action == "stop": @@ -1136,13 +1415,13 @@ class PanelHandler(BaseHandler): elif action == "backup": command = "backup_server" - elif difficulty == 'reaction': - interval_type = 'reaction' - action = bleach.clean(self.get_argument('action', None)) - delay = bleach.clean(self.get_argument('delay', None)) - parent = bleach.clean(self.get_argument('parent', None)) + elif difficulty == "reaction": + interval_type = "reaction" + action = bleach.clean(self.get_argument("action", None)) + delay = bleach.clean(self.get_argument("delay", None)) + parent = bleach.clean(self.get_argument("parent", None)) if action == "command": - command = bleach.clean(self.get_argument('command', None)) + command = bleach.clean(self.get_argument("command", None)) elif action == "start": command = "start_server" elif action == "stop": @@ -1153,16 +1432,18 @@ class PanelHandler(BaseHandler): command = "backup_server" else: - interval_type = '' - cron_string = bleach.clean(self.get_argument('cron', '')) + interval_type = "" + cron_string = bleach.clean(self.get_argument("cron", "")) try: CronValidator.parse(cron_string) except Exception as e: - self.redirect(f"/panel/error?error=INVALID FORMAT: Invalid Cron Format. {e}") + self.redirect( + f"/panel/error?error=INVALID FORMAT: Invalid Cron Format. {e}" + ) return - action = bleach.clean(self.get_argument('action', None)) + action = bleach.clean(self.get_argument("action", None)) if action == "command": - command = bleach.clean(self.get_argument('command', None)) + command = bleach.clean(self.get_argument("command", None)) elif action == "start": command = "start_server" elif action == "stop": @@ -1171,18 +1452,23 @@ class PanelHandler(BaseHandler): command = "restart_server" elif action == "backup": command = "backup_server" - if bleach.clean(self.get_argument('enabled', '0')) == '1': + if bleach.clean(self.get_argument("enabled", "0")) == "1": enabled = True else: enabled = False - if bleach.clean(self.get_argument('one_time', '0')) == '1': + if bleach.clean(self.get_argument("one_time", "0")) == "1": one_time = True else: one_time = False - if not superuser and not permissions['Backup'] in self.controller.server_perms.get_user_id_permissions_list(exec_user["user_id"], - server_id): - self.redirect("/panel/error?error=Unauthorized access: User not authorized") + if not superuser and not permissions[ + "Backup" + ] in self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ): + self.redirect( + "/panel/error?error=Unauthorized access: User not authorized" + ) return elif server_id is None: self.redirect("/panel/error?error=Invalid Server ID") @@ -1203,39 +1489,39 @@ class PanelHandler(BaseHandler): "start_time": sch_time, "enabled": enabled, "one_time": one_time, - "cron_string": '', + "cron_string": "", "parent": None, - "delay": 0 + "delay": 0, } elif difficulty == "reaction": job_data = { "server_id": server_id, "action": action, "interval_type": interval_type, - "interval": '', - #We'll base every interval off of a midnight start time. - "start_time": '', + "interval": "", + # We'll base every interval off of a midnight start time. + "start_time": "", "command": command, - "cron_string": '', + "cron_string": "", "enabled": enabled, "one_time": one_time, "parent": parent, - "delay": delay + "delay": delay, } elif difficulty == "advanced": job_data = { "server_id": server_id, "action": action, - "interval_type": '', - "interval": '', - #We'll base every interval off of a midnight start time. - "start_time": '', + "interval_type": "", + "interval": "", + # We'll base every interval off of a midnight start time. + "start_time": "", "command": command, "cron_string": cron_string, "enabled": enabled, "one_time": one_time, "parent": None, - "delay": 0 + "delay": 0, } else: job_data = { @@ -1245,38 +1531,39 @@ class PanelHandler(BaseHandler): "interval": interval, "command": command, "enabled": enabled, - #We'll base every interval off of a midnight start time. - "start_time": '00:00', + # We'll base every interval off of a midnight start time. + "start_time": "00:00", "one_time": one_time, - "cron_string": '', - 'parent': None, - 'delay': 0 + "cron_string": "", + "parent": None, + "delay": 0, } self.tasks_manager.schedule_job(job_data) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Edited server {server_id}: added scheduled job", - server_id, - self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Edited server {server_id}: added scheduled job", + server_id, + self.get_remote_ip(), + ) self.tasks_manager.reload_schedule_from_db() self.redirect(f"/panel/server_detail?id={server_id}&subpage=schedules") - if page == "edit_schedule": - server_id = bleach.clean(self.get_argument('id', None)) - difficulty = bleach.clean(self.get_argument('difficulty', None)) + server_id = bleach.clean(self.get_argument("id", None)) + difficulty = bleach.clean(self.get_argument("difficulty", None)) server_obj = self.controller.servers.get_server_obj(server_id) - enabled = bleach.clean(self.get_argument('enabled', '0')) - if difficulty == 'basic': - action = bleach.clean(self.get_argument('action', None)) - interval = bleach.clean(self.get_argument('interval', None)) - interval_type = bleach.clean(self.get_argument('interval_type', None)) - #only check for time if it's number of days + enabled = bleach.clean(self.get_argument("enabled", "0")) + if difficulty == "basic": + action = bleach.clean(self.get_argument("action", None)) + interval = bleach.clean(self.get_argument("interval", None)) + interval_type = bleach.clean(self.get_argument("interval_type", None)) + # only check for time if it's number of days if interval_type == "days": - sch_time = bleach.clean(self.get_argument('time', None)) + sch_time = bleach.clean(self.get_argument("time", None)) if action == "command": - command = bleach.clean(self.get_argument('command', None)) + command = bleach.clean(self.get_argument("command", None)) elif action == "start": command = "start_server" elif action == "stop": @@ -1285,13 +1572,13 @@ class PanelHandler(BaseHandler): command = "restart_server" elif action == "backup": command = "backup_server" - elif difficulty == 'reaction': - interval_type = 'reaction' - action = bleach.clean(self.get_argument('action', None)) - delay = bleach.clean(self.get_argument('delay', None)) - parent = bleach.clean(self.get_argument('parent', None)) + elif difficulty == "reaction": + interval_type = "reaction" + action = bleach.clean(self.get_argument("action", None)) + delay = bleach.clean(self.get_argument("delay", None)) + parent = bleach.clean(self.get_argument("parent", None)) if action == "command": - command = bleach.clean(self.get_argument('command', None)) + command = bleach.clean(self.get_argument("command", None)) elif action == "start": command = "start_server" elif action == "stop": @@ -1300,19 +1587,21 @@ class PanelHandler(BaseHandler): command = "restart_server" elif action == "backup": command = "backup_server" - parent = bleach.clean(self.get_argument('parent', None)) + parent = bleach.clean(self.get_argument("parent", None)) else: - interval_type = '' - cron_string = bleach.clean(self.get_argument('cron', '')) - sch_id = self.get_argument('sch_id', None) + interval_type = "" + cron_string = bleach.clean(self.get_argument("cron", "")) + sch_id = self.get_argument("sch_id", None) try: CronValidator.parse(cron_string) except Exception as e: - self.redirect(f"/panel/error?error=INVALID FORMAT: Invalid Cron Format. {e}") + self.redirect( + f"/panel/error?error=INVALID FORMAT: Invalid Cron Format. {e}" + ) return - action = bleach.clean(self.get_argument('action', None)) + action = bleach.clean(self.get_argument("action", None)) if action == "command": - command = bleach.clean(self.get_argument('command', None)) + command = bleach.clean(self.get_argument("command", None)) elif action == "start": command = "start_server" elif action == "stop": @@ -1321,18 +1610,23 @@ class PanelHandler(BaseHandler): command = "restart_server" elif action == "backup": command = "backup_server" - if bleach.clean(self.get_argument('enabled', '0'))=='1': + if bleach.clean(self.get_argument("enabled", "0")) == "1": enabled = True else: enabled = False - if bleach.clean(self.get_argument('one_time', '0')) == '1': + if bleach.clean(self.get_argument("one_time", "0")) == "1": one_time = True else: one_time = False - if not superuser and not permissions['Backup'] in self.controller.server_perms.get_user_id_permissions_list(exec_user["user_id"], - server_id): - self.redirect("/panel/error?error=Unauthorized access: User not authorized") + if not superuser and not permissions[ + "Backup" + ] in self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ): + self.redirect( + "/panel/error?error=Unauthorized access: User not authorized" + ) return elif server_id is None: self.redirect("/panel/error?error=Invalid Server ID") @@ -1353,39 +1647,39 @@ class PanelHandler(BaseHandler): "start_time": sch_time, "enabled": enabled, "one_time": one_time, - "cron_string": '', + "cron_string": "", "parent": None, - "delay": 0 + "delay": 0, } elif difficulty == "advanced": job_data = { "server_id": server_id, "action": action, - "interval_type": '', - "interval": '', - #We'll base every interval off of a midnight start time. - "start_time": '', + "interval_type": "", + "interval": "", + # We'll base every interval off of a midnight start time. + "start_time": "", "command": command, "cron_string": cron_string, - "delay": '', - "parent": '', + "delay": "", + "parent": "", "enabled": enabled, - "one_time": one_time + "one_time": one_time, } elif difficulty == "reaction": job_data = { "server_id": server_id, "action": action, "interval_type": interval_type, - "interval": '', - #We'll base every interval off of a midnight start time. - "start_time": '', + "interval": "", + # We'll base every interval off of a midnight start time. + "start_time": "", "command": command, - "cron_string": '', + "cron_string": "", "enabled": enabled, "one_time": one_time, "parent": parent, - "delay": delay + "delay": delay, } else: job_data = { @@ -1395,52 +1689,59 @@ class PanelHandler(BaseHandler): "interval": interval, "command": command, "enabled": enabled, - #We'll base every interval off of a midnight start time. - "start_time": '00:00', - "delay": '', - "parent": '', + # We'll base every interval off of a midnight start time. + "start_time": "00:00", + "delay": "", + "parent": "", "one_time": one_time, - "cron_string": '' + "cron_string": "", } - sch_id = self.get_argument('sch_id', None) + sch_id = self.get_argument("sch_id", None) self.tasks_manager.update_job(sch_id, job_data) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Edited server {server_id}: updated schedule", - server_id, - self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Edited server {server_id}: updated schedule", + server_id, + self.get_remote_ip(), + ) self.tasks_manager.reload_schedule_from_db() self.redirect(f"/panel/server_detail?id={server_id}&subpage=schedules") - elif page == "edit_user": - if bleach.clean(self.get_argument('username', None)) == 'system': - self.redirect("/panel/error?error=Unauthorized access: system user is not editable") - user_id = bleach.clean(self.get_argument('id', None)) - username = bleach.clean(self.get_argument('username', None)) - password0 = bleach.clean(self.get_argument('password0', None)) - password1 = bleach.clean(self.get_argument('password1', None)) - email = bleach.clean(self.get_argument('email', "default@example.com")) - enabled = int(float(self.get_argument('enabled', '0'))) - lang = bleach.clean(self.get_argument('language'), helper.get_setting('language')) + if bleach.clean(self.get_argument("username", None)) == "system": + self.redirect( + "/panel/error?error=Unauthorized access: system user is not editable" + ) + user_id = bleach.clean(self.get_argument("id", None)) + username = bleach.clean(self.get_argument("username", None)) + password0 = bleach.clean(self.get_argument("password0", None)) + password1 = bleach.clean(self.get_argument("password1", None)) + email = bleach.clean(self.get_argument("email", "default@example.com")) + enabled = int(float(self.get_argument("enabled", "0"))) + lang = bleach.clean( + self.get_argument("language"), helper.get_setting("language") + ) if superuser: - #Checks if user is trying to change super user status of self. We don't want that. + # Checks if user is trying to change super user status of self. We don't want that. # Automatically make them stay super user since we know they are. - if str(exec_user['user_id']) != str(user_id): - superuser = bleach.clean(self.get_argument('superuser', '0')) + if str(exec_user["user_id"]) != str(user_id): + superuser = bleach.clean(self.get_argument("superuser", "0")) else: - superuser = '1' + superuser = "1" else: - superuser = '0' - if superuser == '1': + superuser = "0" + if superuser == "1": superuser = True else: superuser = False if Enum_Permissions_Crafty.User_Config not in exec_user_crafty_permissions: if str(user_id) != str(exec_user["user_id"]): - self.redirect("/panel/error?error=Unauthorized access: not a user editor") + self.redirect( + "/panel/error?error=Unauthorized access: not a user editor" + ) return user_data = { @@ -1450,10 +1751,12 @@ class PanelHandler(BaseHandler): } self.controller.users.update_user(user_id, user_data=user_data) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Edited user {username} (UID:{user_id}) password", - server_id=0, - source_ip=self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Edited user {username} (UID:{user_id}) password", + server_id=0, + source_ip=self.get_remote_ip(), + ) self.redirect("/panel/panel_config") return elif username is None or username == "": @@ -1489,20 +1792,24 @@ class PanelHandler(BaseHandler): } user_crafty_data = { "permissions_mask": permissions_mask, - "server_quantity": server_quantity + "server_quantity": server_quantity, } - self.controller.users.update_user(user_id, user_data=user_data, user_crafty_data=user_crafty_data) + self.controller.users.update_user( + user_id, user_data=user_data, user_crafty_data=user_crafty_data + ) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Edited user {username} (UID:{user_id}) with roles {roles} and permissions {permissions_mask}", - server_id=0, - source_ip=self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Edited user {username} (UID:{user_id}) with roles {roles} and permissions {permissions_mask}", + server_id=0, + source_ip=self.get_remote_ip(), + ) self.redirect("/panel/panel_config") elif page == "edit_user_apikeys": - user_id = self.get_argument('id', None) - name = self.get_argument('name', None) - superuser = self.get_argument('superuser', None) == '1' + user_id = self.get_argument("id", None) + name = self.get_argument("name", None) + superuser = self.get_argument("superuser", None) == "1" if name is None or name == "": self.redirect("/panel/error?error=Invalid API key name") @@ -1519,17 +1826,25 @@ class PanelHandler(BaseHandler): crafty_permissions_mask = self.get_perms() server_permissions_mask = self.get_perms_server() - self.controller.users.add_user_api_key(name, user_id, superuser, crafty_permissions_mask, server_permissions_mask) + self.controller.users.add_user_api_key( + name, + user_id, + superuser, + crafty_permissions_mask, + server_permissions_mask, + ) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Added API key {name} with crafty permissions {crafty_permissions_mask}" + - f" and {server_permissions_mask} for user with UID: {user_id}", - server_id=0, - source_ip=self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Added API key {name} with crafty permissions {crafty_permissions_mask}" + + f" and {server_permissions_mask} for user with UID: {user_id}", + server_id=0, + source_ip=self.get_remote_ip(), + ) self.redirect(f"/panel/edit_user_apikeys?id={user_id}") elif page == "get_token": - key_id = self.get_argument('id', None) + key_id = self.get_argument("id", None) if key_id is None: self.redirect("/panel/error?error=Invalid Key ID") @@ -1541,39 +1856,46 @@ class PanelHandler(BaseHandler): self.redirect("/panel/error?error=Invalid Key ID") return - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Generated a new API token for the key {key.name} from user with UID: {key.user.user_id}", - server_id=0, - source_ip=self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Generated a new API token for the key {key.name} from user with UID: {key.user.user_id}", + server_id=0, + source_ip=self.get_remote_ip(), + ) - self.write(authentication.generate(key.user.user_id, { - 'token_id': key.token_id - })) + self.write( + authentication.generate(key.user.user_id, {"token_id": key.token_id}) + ) self.finish() - elif page == "add_user": - if bleach.clean(self.get_argument('username', None)).lower() == 'system': - self.redirect("/panel/error?error=Unauthorized access: username system is reserved for the Crafty system." + - " Please choose a different username.") + if bleach.clean(self.get_argument("username", None)).lower() == "system": + self.redirect( + "/panel/error?error=Unauthorized access: username system is reserved for the Crafty system." + + " Please choose a different username." + ) return - username = bleach.clean(self.get_argument('username', None)) - password0 = bleach.clean(self.get_argument('password0', None)) - password1 = bleach.clean(self.get_argument('password1', None)) - email = bleach.clean(self.get_argument('email', "default@example.com")) - enabled = int(float(self.get_argument('enabled', '0'))) - lang = bleach.clean(self.get_argument('lang', helper.get_setting('language'))) + username = bleach.clean(self.get_argument("username", None)) + password0 = bleach.clean(self.get_argument("password0", None)) + password1 = bleach.clean(self.get_argument("password1", None)) + email = bleach.clean(self.get_argument("email", "default@example.com")) + enabled = int(float(self.get_argument("enabled", "0"))) + lang = bleach.clean( + self.get_argument("lang", helper.get_setting("language")) + ) if superuser: - superuser = bleach.clean(self.get_argument('superuser', '0')) + superuser = bleach.clean(self.get_argument("superuser", "0")) else: - superuser = '0' - if superuser == '1': + superuser = "0" + if superuser == "1": superuser = True else: superuser = False if Enum_Permissions_Crafty.User_Config not in exec_user_crafty_permissions: - self.redirect("/panel/error?error=Unauthorized access: not a user editor") + self.redirect( + "/panel/error?error=Unauthorized access: not a user editor" + ) return elif username is None or username == "": self.redirect("/panel/error?error=Invalid username") @@ -1591,33 +1913,47 @@ class PanelHandler(BaseHandler): roles = self.get_user_role_memberships() permissions_mask, server_quantity = self.get_perms_quantity() - user_id = self.controller.users.add_user(username, password=password0, email=email, enabled=enabled, superuser=superuser) + user_id = self.controller.users.add_user( + username, + password=password0, + email=email, + enabled=enabled, + superuser=superuser, + ) user_data = { "roles": roles, - 'lang': lang, + "lang": lang, } user_crafty_data = { "permissions_mask": permissions_mask, - "server_quantity": server_quantity + "server_quantity": server_quantity, } - self.controller.users.update_user(user_id, user_data=user_data, user_crafty_data=user_crafty_data) + self.controller.users.update_user( + user_id, user_data=user_data, user_crafty_data=user_crafty_data + ) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Added user {username} (UID:{user_id})", - server_id=0, - source_ip=self.get_remote_ip()) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Edited user {username} (UID:{user_id}) with roles {roles}", - server_id=0, - source_ip=self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Added user {username} (UID:{user_id})", + server_id=0, + source_ip=self.get_remote_ip(), + ) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Edited user {username} (UID:{user_id}) with roles {roles}", + server_id=0, + source_ip=self.get_remote_ip(), + ) self.redirect("/panel/panel_config") elif page == "edit_role": - role_id = bleach.clean(self.get_argument('id', None)) - role_name = bleach.clean(self.get_argument('role_name', None)) + role_id = bleach.clean(self.get_argument("id", None)) + role_name = bleach.clean(self.get_argument("role_name", None)) if Enum_Permissions_Crafty.Roles_Config not in exec_user_crafty_permissions: - self.redirect("/panel/error?error=Unauthorized access: not a role editor") + self.redirect( + "/panel/error?error=Unauthorized access: not a role editor" + ) return elif role_name is None or role_name == "": self.redirect("/panel/error?error=Invalid username") @@ -1634,24 +1970,26 @@ class PanelHandler(BaseHandler): servers = self.get_role_servers() permissions_mask = self.get_perms_server() - role_data = { - "role_name": role_name, - "servers": servers - } - self.controller.roles.update_role(role_id, role_data=role_data, permissions_mask=permissions_mask) + role_data = {"role_name": role_name, "servers": servers} + self.controller.roles.update_role( + role_id, role_data=role_data, permissions_mask=permissions_mask + ) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Edited role {role_name} (RID:{role_id}) with servers {servers}", - server_id=0, - source_ip=self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Edited role {role_name} (RID:{role_id}) with servers {servers}", + server_id=0, + source_ip=self.get_remote_ip(), + ) self.redirect("/panel/panel_config") - elif page == "add_role": - role_name = bleach.clean(self.get_argument('role_name', None)) + role_name = bleach.clean(self.get_argument("role_name", None)) if Enum_Permissions_Crafty.Roles_Config not in exec_user_crafty_permissions: - self.redirect("/panel/error?error=Unauthorized access: not a role editor") + self.redirect( + "/panel/error?error=Unauthorized access: not a role editor" + ) return elif role_name is None or role_name == "": self.redirect("/panel/error?error=Invalid role name") @@ -1666,55 +2004,63 @@ class PanelHandler(BaseHandler): permissions_mask = self.get_perms_server() role_id = self.controller.roles.add_role(role_name) - self.controller.roles.update_role(role_id, {"servers": servers}, permissions_mask) + self.controller.roles.update_role( + role_id, {"servers": servers}, permissions_mask + ) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Added role {role_name} (RID:{role_id})", - server_id=0, - source_ip=self.get_remote_ip()) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Edited role {role_name} (RID:{role_id}) with servers {servers}", - server_id=0, - source_ip=self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Added role {role_name} (RID:{role_id})", + server_id=0, + source_ip=self.get_remote_ip(), + ) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Edited role {role_name} (RID:{role_id}) with servers {servers}", + server_id=0, + source_ip=self.get_remote_ip(), + ) self.redirect("/panel/panel_config") else: self.set_status(404) page_data = { - 'lang': helper.get_setting('language'), - 'lang_page': helper.getLangPage(helper.get_setting('language')), + "lang": helper.get_setting("language"), + "lang_page": helper.getLangPage(helper.get_setting("language")), } self.render( - "public/404.html", - translate=self.translator.translate, - data=page_data + "public/404.html", translate=self.translator.translate, data=page_data ) @tornado.web.authenticated def delete(self, page): api_key, _token_data, exec_user = self.current_user - superuser = exec_user['superuser'] + superuser = exec_user["superuser"] if api_key is not None: superuser = superuser and api_key.superuser page_data = { # todo: make this actually pull and compare version data - 'update_available': False, - 'version_data': helper.get_version_string(), - 'user_data': exec_user, - 'hosts_data': self.controller.management.get_latest_hosts_stats(), - 'show_contribute': helper.get_setting("show_contribute_link", True), - 'lang': self.controller.users.get_user_lang_by_id(exec_user["user_id"]), - 'lang_page': helper.getLangPage(self.controller.users.get_user_lang_by_id(exec_user["user_id"])), + "update_available": False, + "version_data": helper.get_version_string(), + "user_data": exec_user, + "hosts_data": self.controller.management.get_latest_hosts_stats(), + "show_contribute": helper.get_setting("show_contribute_link", True), + "lang": self.controller.users.get_user_lang_by_id(exec_user["user_id"]), + "lang_page": helper.getLangPage( + self.controller.users.get_user_lang_by_id(exec_user["user_id"]) + ), } if page == "remove_apikey": - key_id = bleach.clean(self.get_argument('id', None)) + key_id = bleach.clean(self.get_argument("id", None)) if not superuser: self.redirect("/panel/error?error=Unauthorized access: not superuser") return - elif key_id is None or self.controller.users.get_user_api_key(key_id) is None: + elif ( + key_id is None or self.controller.users.get_user_api_key(key_id) is None + ): self.redirect("/panel/error?error=Invalid Key ID") return else: @@ -1726,10 +2072,12 @@ class PanelHandler(BaseHandler): self.controller.users.delete_user_api_key(key_id) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"Removed API key {target_key} (ID: {key_id}) from user {exec_user['user_id']}", - server_id=0, - source_ip=self.get_remote_ip()) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Removed API key {target_key} (ID: {key_id}) from user {exec_user['user_id']}", + server_id=0, + source_ip=self.get_remote_ip(), + ) self.redirect("/panel/panel_config") else: self.set_status(404) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 80363afb..f4d392de 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -14,36 +14,39 @@ except ModuleNotFoundError as e: logger = logging.getLogger(__name__) -class PublicHandler(BaseHandler): +class PublicHandler(BaseHandler): def set_current_user(self, user_id: str = None): - expire_days = helper.get_setting('cookie_expire') + expire_days = helper.get_setting("cookie_expire") # if helper comes back with false if not expire_days: expire_days = "5" if user_id is not None: - self.set_cookie("token", authentication.generate(user_id), expires_days=int(expire_days)) + self.set_cookie( + "token", authentication.generate(user_id), expires_days=int(expire_days) + ) else: self.clear_cookie("token") - #self.clear_cookie("user") - #self.clear_cookie("user_data") + # self.clear_cookie("user") + # self.clear_cookie("user_data") def get(self, page=None): - error = bleach.clean(self.get_argument('error', "Invalid Login!")) - error_msg = bleach.clean(self.get_argument('error_msg', '')) + error = bleach.clean(self.get_argument("error", "Invalid Login!")) + error_msg = bleach.clean(self.get_argument("error_msg", "")) page_data = { - 'version': helper.get_version_string(), - 'error': error, 'lang': helper.get_setting('language'), - 'lang_page': helper.getLangPage(helper.get_setting('language')), - 'query': "" + "version": helper.get_version_string(), + "error": error, + "lang": helper.get_setting("language"), + "lang_page": helper.getLangPage(helper.get_setting("language")), + "query": "", } if self.request.query: - page_data['query'] = self.request.query + page_data["query"] = self.request.query # sensible defaults template = "public/404.html" @@ -59,75 +62,81 @@ class PublicHandler(BaseHandler): elif page == "logout": self.clear_cookie("token") - #self.clear_cookie("user") - #self.clear_cookie("user_data") - self.redirect('/public/login') + # self.clear_cookie("user") + # self.clear_cookie("user_data") + self.redirect("/public/login") return # if we have no page, let's go to login else: if self.request.query: - self.redirect('/public/login?'+self.request.query) + self.redirect("/public/login?" + self.request.query) else: - self.redirect('/public/login') + self.redirect("/public/login") return self.render( template, data=page_data, translate=self.translator.translate, - error_msg = error_msg + error_msg=error_msg, ) def post(self, page=None): - error = bleach.clean(self.get_argument('error', "Invalid Login!")) - error_msg = bleach.clean(self.get_argument('error_msg', '')) + error = bleach.clean(self.get_argument("error", "Invalid Login!")) + error_msg = bleach.clean(self.get_argument("error_msg", "")) page_data = { - 'version': helper.get_version_string(), - 'error': error, 'lang': helper.get_setting('language'), - 'lang_page': helper.getLangPage(helper.get_setting('language')), - 'query': "" + "version": helper.get_version_string(), + "error": error, + "lang": helper.get_setting("language"), + "lang_page": helper.getLangPage(helper.get_setting("language")), + "query": "", } if self.request.query: - page_data['query'] = self.request.query + page_data["query"] = self.request.query - if page == 'login': + if page == "login": next_page = "/public/login" if self.request.query: - next_page = '/public/login?'+self.request.query + next_page = "/public/login?" + self.request.query - entered_username = bleach.clean(self.get_argument('username')) - entered_password = bleach.clean(self.get_argument('password')) + entered_username = bleach.clean(self.get_argument("username")) + entered_password = bleach.clean(self.get_argument("password")) # pylint: disable=no-member - user_data = Users.get_or_none(fn.Lower(Users.username) == entered_username.lower()) - + user_data = Users.get_or_none( + fn.Lower(Users.username) == entered_username.lower() + ) # if we don't have a user if not user_data: error_msg = "Incorrect username or password. Please try again." - #self.clear_cookie("user") - #self.clear_cookie("user_data") + # self.clear_cookie("user") + # self.clear_cookie("user_data") self.clear_cookie("token") if self.request.query: - self.redirect(f'/public/login?error_msg={error_msg}&{self.request.query}') + self.redirect( + f"/public/login?error_msg={error_msg}&{self.request.query}" + ) else: - self.redirect(f'/public/login?error_msg={error_msg}') + self.redirect(f"/public/login?error_msg={error_msg}") return # if they are disabled if not user_data.enabled: error_msg = "User account disabled. Please contact your system administrator for more info." - #self.clear_cookie("user") - #self.clear_cookie("user_data") + # self.clear_cookie("user") + # self.clear_cookie("user_data") self.clear_cookie("token") if self.request.query: - self.redirect(f'/public/login?error_msg={error_msg}&{self.request.query}') + self.redirect( + f"/public/login?error_msg={error_msg}&{self.request.query}" + ) else: - self.redirect(f'/public/login?error_msg={error_msg}') + self.redirect(f"/public/login?error_msg={error_msg}") return login_result = helper.verify_pass(entered_password, user_data.password) @@ -135,37 +144,48 @@ class PublicHandler(BaseHandler): # Valid Login if login_result: self.set_current_user(user_data.user_id) - logger.info(f"User: {user_data} Logged in from IP: {self.get_remote_ip()}") + logger.info( + f"User: {user_data} Logged in from IP: {self.get_remote_ip()}" + ) # record this login - q = Users.select().where(Users.username == entered_username.lower()).get() + q = ( + Users.select() + .where(Users.username == entered_username.lower()) + .get() + ) q.last_ip = self.get_remote_ip() q.last_login = helper.get_time_as_string() q.save() # log this login - self.controller.management.add_to_audit_log(user_data.user_id, "Logged in", 0, self.get_remote_ip()) + self.controller.management.add_to_audit_log( + user_data.user_id, "Logged in", 0, self.get_remote_ip() + ) - - if self.request.query_arguments.get('next'): - next_page = self.request.query_arguments.get('next')[0].decode() + if self.request.query_arguments.get("next"): + next_page = self.request.query_arguments.get("next")[0].decode() else: next_page = "/panel/dashboard" self.redirect(next_page) else: - #self.clear_cookie("user") - #self.clear_cookie("user_data") + # self.clear_cookie("user") + # self.clear_cookie("user_data") self.clear_cookie("token") error_msg = "Inncorrect username or password. Please try again." # log this failed login attempt - self.controller.management.add_to_audit_log(user_data.user_id, "Tried to log in", 0, self.get_remote_ip()) + self.controller.management.add_to_audit_log( + user_data.user_id, "Tried to log in", 0, self.get_remote_ip() + ) if self.request.query: - self.redirect(f'/public/login?error_msg={error_msg}&{self.request.query}') + self.redirect( + f"/public/login?error_msg={error_msg}&{self.request.query}" + ) else: - self.redirect(f'/public/login?error_msg={error_msg}') + self.redirect(f"/public/login?error_msg={error_msg}") else: if self.request.query: - self.redirect('/public/login?'+self.request.query) + self.redirect("/public/login?" + self.request.query) else: - self.redirect('/public/login') + self.redirect("/public/login") diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index de900c07..feb98075 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -20,13 +20,13 @@ except ModuleNotFoundError as e: logger = logging.getLogger(__name__) -class ServerHandler(BaseHandler): +class ServerHandler(BaseHandler): @tornado.web.authenticated def get(self, page): # pylint: disable=unused-variable api_key, token_data, exec_user = self.current_user - superuser = exec_user['superuser'] + superuser = exec_user["superuser"] if api_key is not None: superuser = superuser and api_key.superuser @@ -34,61 +34,82 @@ class ServerHandler(BaseHandler): if superuser: defined_servers = self.controller.list_defined_servers() exec_user_role.add("Super User") - exec_user_crafty_permissions = self.controller.crafty_perms.list_defined_crafty_permissions() + exec_user_crafty_permissions = ( + self.controller.crafty_perms.list_defined_crafty_permissions() + ) list_roles = [] for role in self.controller.roles.get_all_roles(): list_roles.append(self.controller.roles.get_role(role.role_id)) else: - exec_user_crafty_permissions = self.controller.crafty_perms.get_crafty_permissions_list(exec_user["user_id"]) - defined_servers = self.controller.servers.get_authorized_servers(exec_user["user_id"]) + exec_user_crafty_permissions = ( + self.controller.crafty_perms.get_crafty_permissions_list( + exec_user["user_id"] + ) + ) + defined_servers = self.controller.servers.get_authorized_servers( + exec_user["user_id"] + ) list_roles = [] - for r in exec_user['roles']: + for r in exec_user["roles"]: role = self.controller.roles.get_role(r) - exec_user_role.add(role['role_name']) - list_roles.append(self.controller.roles.get_role(role['role_id'])) + exec_user_role.add(role["role_name"]) + list_roles.append(self.controller.roles.get_role(role["role_id"])) template = "public/404.html" page_data = { - 'version_data': helper.get_version_string(), - 'user_data': exec_user, - 'user_role' : exec_user_role, - 'roles' : list_roles, - 'user_crafty_permissions' : exec_user_crafty_permissions, - 'crafty_permissions': { - 'Server_Creation': Enum_Permissions_Crafty.Server_Creation, - 'User_Config': Enum_Permissions_Crafty.User_Config, - 'Roles_Config': Enum_Permissions_Crafty.Roles_Config, + "version_data": helper.get_version_string(), + "user_data": exec_user, + "user_role": exec_user_role, + "roles": list_roles, + "user_crafty_permissions": exec_user_crafty_permissions, + "crafty_permissions": { + "Server_Creation": Enum_Permissions_Crafty.Server_Creation, + "User_Config": Enum_Permissions_Crafty.User_Config, + "Roles_Config": Enum_Permissions_Crafty.Roles_Config, }, - 'server_stats': { - 'total': len(self.controller.list_defined_servers()), - 'running': len(self.controller.list_running_servers()), - 'stopped': (len(self.controller.list_defined_servers()) - len(self.controller.list_running_servers())) + "server_stats": { + "total": len(self.controller.list_defined_servers()), + "running": len(self.controller.list_running_servers()), + "stopped": ( + len(self.controller.list_defined_servers()) + - len(self.controller.list_running_servers()) + ), }, - 'hosts_data': self.controller.management.get_latest_hosts_stats(), - 'menu_servers': defined_servers, - 'show_contribute': helper.get_setting("show_contribute_link", True), - 'lang': self.controller.users.get_user_lang_by_id(exec_user["user_id"]), - 'lang_page': helper.getLangPage(self.controller.users.get_user_lang_by_id(exec_user["user_id"])), - 'api_key': { - 'name': api_key.name, - 'created': api_key.created, - 'server_permissions': api_key.server_permissions, - 'crafty_permissions': api_key.crafty_permissions, - 'superuser': api_key.superuser - } if api_key is not None else None, - 'superuser': superuser + "hosts_data": self.controller.management.get_latest_hosts_stats(), + "menu_servers": defined_servers, + "show_contribute": helper.get_setting("show_contribute_link", True), + "lang": self.controller.users.get_user_lang_by_id(exec_user["user_id"]), + "lang_page": helper.getLangPage( + self.controller.users.get_user_lang_by_id(exec_user["user_id"]) + ), + "api_key": { + "name": api_key.name, + "created": api_key.created, + "server_permissions": api_key.server_permissions, + "crafty_permissions": api_key.crafty_permissions, + "superuser": api_key.superuser, + } + if api_key is not None + else None, + "superuser": superuser, } - if helper.get_setting("allow_nsfw_profile_pictures"): + if helper.get_setting("allow_nsfw_profile_pictures"): rating = "x" else: rating = "g" - - if exec_user['email'] != 'default@example.com' or "": - g = libgravatar.Gravatar(libgravatar.sanitize_email(exec_user['email'])) - url = g.get_image(size=80, default="404", force_default=False, rating=rating, filetype_extension=False, use_ssl=True) # + "?d=404" + if exec_user["email"] != "default@example.com" or "": + g = libgravatar.Gravatar(libgravatar.sanitize_email(exec_user["email"])) + url = g.get_image( + size=80, + default="404", + force_default=False, + rating=rating, + filetype_extension=False, + use_ssl=True, + ) # + "?d=404" if requests.head(url).status_code != 404: profile_url = url else: @@ -96,22 +117,32 @@ class ServerHandler(BaseHandler): else: profile_url = "/static/assets/images/faces-clipart/pic-3.png" - page_data['user_image'] = profile_url + page_data["user_image"] = profile_url if superuser: - page_data['roles'] = list_roles + page_data["roles"] = list_roles if page == "step1": - if not superuser and not self.controller.crafty_perms.can_create_server(exec_user["user_id"]): - self.redirect("/panel/error?error=Unauthorized access: not a server creator or server limit reached") + if not superuser and not self.controller.crafty_perms.can_create_server( + exec_user["user_id"] + ): + self.redirect( + "/panel/error?error=Unauthorized access: not a server creator or server limit reached" + ) return - page_data['server_types'] = server_jar_obj.get_serverjar_data() - page_data['js_server_types'] = json.dumps(server_jar_obj.get_serverjar_data()) + page_data["server_types"] = server_jar_obj.get_serverjar_data() + page_data["js_server_types"] = json.dumps( + server_jar_obj.get_serverjar_data() + ) template = "server/wizard.html" if page == "bedrock_step1": - if not superuser and not self.controller.crafty_perms.can_create_server(exec_user["user_id"]): - self.redirect("/panel/error?error=Unauthorized access: not a server creator or server limit reached") + if not superuser and not self.controller.crafty_perms.can_create_server( + exec_user["user_id"] + ): + self.redirect( + "/panel/error?error=Unauthorized access: not a server creator or server limit reached" + ) return template = "server/bedrock_wizard.html" @@ -126,17 +157,19 @@ class ServerHandler(BaseHandler): def post(self, page): # pylint: disable=unused-variable api_key, token_data, exec_user = self.current_user - superuser = exec_user['superuser'] + superuser = exec_user["superuser"] if api_key is not None: superuser = superuser and api_key.superuser template = "public/404.html" page_data = { - 'version_data': "version_data_here", # TODO - 'user_data': exec_user, - 'show_contribute': helper.get_setting("show_contribute_link", True), - 'lang': self.controller.users.get_user_lang_by_id(exec_user["user_id"]), - 'lang_page': helper.getLangPage(self.controller.users.get_user_lang_by_id(exec_user["user_id"])) + "version_data": "version_data_here", # TODO + "user_data": exec_user, + "show_contribute": helper.get_setting("show_contribute_link", True), + "lang": self.controller.users.get_user_lang_by_id(exec_user["user_id"]), + "lang_page": helper.getLangPage( + self.controller.users.get_user_lang_by_id(exec_user["user_id"]) + ), } if page == "command": @@ -145,53 +178,68 @@ class ServerHandler(BaseHandler): if server_id is not None: if command == "clone_server": + def is_name_used(name): for server in self.controller.servers.get_all_defined_servers(): - if server['server_name'] == name: + if server["server_name"] == name: return True return - server_data = self.controller.servers.get_server_data_by_id(server_id) - server_uuid = server_data.get('server_uuid') - new_server_name = server_data.get('server_name') + " (Copy)" + server_data = self.controller.servers.get_server_data_by_id( + server_id + ) + server_uuid = server_data.get("server_uuid") + new_server_name = server_data.get("server_name") + " (Copy)" name_counter = 1 while is_name_used(new_server_name): name_counter += 1 - new_server_name = server_data.get('server_name') + f" (Copy {name_counter})" + new_server_name = ( + server_data.get("server_name") + f" (Copy {name_counter})" + ) new_server_uuid = helper.create_uuid() - while os.path.exists(os.path.join(helper.servers_dir, new_server_uuid)): + while os.path.exists( + os.path.join(helper.servers_dir, new_server_uuid) + ): new_server_uuid = helper.create_uuid() new_server_path = os.path.join(helper.servers_dir, new_server_uuid) # copy the old server - file_helper.copy_dir(server_data.get('path'), new_server_path) + file_helper.copy_dir(server_data.get("path"), new_server_path) # TODO get old server DB data to individual variables - stop_command = server_data.get('stop_command') - new_server_command = str(server_data.get('execution_command')).replace(server_uuid, new_server_uuid) - new_executable = server_data.get('executable') - new_server_log_file = str(helper.get_os_understandable_path(server_data.get('log_path'))).replace(server_uuid, new_server_uuid) - server_port = server_data.get('server_port') - server_type = server_data.get('type') + stop_command = server_data.get("stop_command") + new_server_command = str( + server_data.get("execution_command") + ).replace(server_uuid, new_server_uuid) + new_executable = server_data.get("executable") + new_server_log_file = str( + helper.get_os_understandable_path(server_data.get("log_path")) + ).replace(server_uuid, new_server_uuid) + server_port = server_data.get("server_port") + server_type = server_data.get("type") - self.controller.servers.create_server(new_server_name, - new_server_uuid, - new_server_path, - "", - new_server_command, - new_executable, - new_server_log_file, - stop_command, - server_type, - server_port) + self.controller.servers.create_server( + new_server_name, + new_server_uuid, + new_server_path, + "", + new_server_command, + new_executable, + new_server_log_file, + stop_command, + server_type, + server_port, + ) self.controller.init_all_servers() return - self.controller.management.send_command(exec_user['user_id'], server_id, self.get_remote_ip(), command) + self.controller.management.send_command( + exec_user["user_id"], server_id, self.get_remote_ip(), command + ) if page == "step1": @@ -199,54 +247,73 @@ class ServerHandler(BaseHandler): user_roles = self.controller.roles.get_all_roles() else: user_roles = self.controller.roles.get_all_roles() - server = bleach.clean(self.get_argument('server', '')) - server_name = bleach.clean(self.get_argument('server_name', '')) - min_mem = bleach.clean(self.get_argument('min_memory', '')) - max_mem = bleach.clean(self.get_argument('max_memory', '')) - port = bleach.clean(self.get_argument('port', '')) - import_type = bleach.clean(self.get_argument('create_type', '')) - import_server_path = bleach.clean(self.get_argument('server_path', '')) - import_server_jar = bleach.clean(self.get_argument('server_jar', '')) + server = bleach.clean(self.get_argument("server", "")) + server_name = bleach.clean(self.get_argument("server_name", "")) + min_mem = bleach.clean(self.get_argument("min_memory", "")) + max_mem = bleach.clean(self.get_argument("max_memory", "")) + port = bleach.clean(self.get_argument("port", "")) + import_type = bleach.clean(self.get_argument("create_type", "")) + import_server_path = bleach.clean(self.get_argument("server_path", "")) + import_server_jar = bleach.clean(self.get_argument("server_jar", "")) server_parts = server.split("|") captured_roles = [] for role in user_roles: - if bleach.clean(self.get_argument(str(role), '')) == "on": + if bleach.clean(self.get_argument(str(role), "")) == "on": captured_roles.append(role) if not server_name: self.redirect("/panel/error?error=Server name cannot be empty!") return - if import_type == 'import_jar': - good_path = self.controller.verify_jar_server(import_server_path, import_server_jar) + if import_type == "import_jar": + good_path = self.controller.verify_jar_server( + import_server_path, import_server_jar + ) if not good_path: - self.redirect("/panel/error?error=Server path or Server Jar not found!") + self.redirect( + "/panel/error?error=Server path or Server Jar not found!" + ) return - new_server_id = self.controller.import_jar_server(server_name, import_server_path,import_server_jar, min_mem, max_mem, port) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"imported a jar server named \"{server_name}\"", # Example: Admin imported a server named "old creative" - new_server_id, - self.get_remote_ip()) - elif import_type == 'import_zip': + new_server_id = self.controller.import_jar_server( + server_name, + import_server_path, + import_server_jar, + min_mem, + max_mem, + port, + ) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f'imported a jar server named "{server_name}"', # Example: Admin imported a server named "old creative" + new_server_id, + self.get_remote_ip(), + ) + elif import_type == "import_zip": # here import_server_path means the zip path - zip_path = bleach.clean(self.get_argument('root_path')) + zip_path = bleach.clean(self.get_argument("root_path")) good_path = helper.check_path_exists(zip_path) if not good_path: self.redirect("/panel/error?error=Temp path not found!") return - new_server_id = self.controller.import_zip_server(server_name, zip_path, import_server_jar, min_mem, max_mem, port) + new_server_id = self.controller.import_zip_server( + server_name, zip_path, import_server_jar, min_mem, max_mem, port + ) if new_server_id == "false": - self.redirect("/panel/error?error=Zip file not accessible! You can fix this permissions issue with" + - f"sudo chown -R crafty:crafty {import_server_path} And sudo chmod 2775 -R {import_server_path}") + self.redirect( + "/panel/error?error=Zip file not accessible! You can fix this permissions issue with" + + f"sudo chown -R crafty:crafty {import_server_path} And sudo chmod 2775 -R {import_server_path}" + ) return - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"imported a zip server named \"{server_name}\"", # Example: Admin imported a server named "old creative" - new_server_id, - self.get_remote_ip()) - #deletes temp dir + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f'imported a zip server named "{server_name}"', # Example: Admin imported a server named "old creative" + new_server_id, + self.get_remote_ip(), + ) + # deletes temp dir file_helper.del_dirs(zip_path) else: if len(server_parts) != 2: @@ -255,26 +322,42 @@ class ServerHandler(BaseHandler): server_type, server_version = server_parts # TODO: add server type check here and call the correct server add functions if not a jar role_ids = self.controller.users.get_user_roles_id(exec_user["user_id"]) - new_server_id = self.controller.create_jar_server(server_type, server_version, server_name, min_mem, max_mem, port) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"created a {server_version} {str(server_type).capitalize()} server named \"{server_name}\"", - # Example: Admin created a 1.16.5 Bukkit server named "survival" - new_server_id, - self.get_remote_ip()) + new_server_id = self.controller.create_jar_server( + server_type, server_version, server_name, min_mem, max_mem, port + ) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f'created a {server_version} {str(server_type).capitalize()} server named "{server_name}"', + # Example: Admin created a 1.16.5 Bukkit server named "survival" + new_server_id, + self.get_remote_ip(), + ) # These lines create a new Role for the Server with full permissions and add the user to it if he's not a superuser if len(captured_roles) == 0: if not superuser: - new_server_uuid = self.controller.servers.get_server_data_by_id(new_server_id).get("server_uuid") - role_id = self.controller.roles.add_role(f"Creator of Server with uuid={new_server_uuid}") - self.controller.server_perms.add_role_server(new_server_id, role_id, "11111111") - self.controller.users.add_role_to_user(exec_user["user_id"], role_id) - self.controller.crafty_perms.add_server_creation(exec_user["user_id"]) + new_server_uuid = self.controller.servers.get_server_data_by_id( + new_server_id + ).get("server_uuid") + role_id = self.controller.roles.add_role( + f"Creator of Server with uuid={new_server_uuid}" + ) + self.controller.server_perms.add_role_server( + new_server_id, role_id, "11111111" + ) + self.controller.users.add_role_to_user( + exec_user["user_id"], role_id + ) + self.controller.crafty_perms.add_server_creation( + exec_user["user_id"] + ) else: for role in captured_roles: role_id = role - self.controller.server_perms.add_role_server(new_server_id, role_id, "11111111") + self.controller.server_perms.add_role_server( + new_server_id, role_id, "11111111" + ) self.controller.stats.record_stats() self.redirect("/panel/dashboard") @@ -284,52 +367,66 @@ class ServerHandler(BaseHandler): user_roles = self.controller.roles.get_all_roles() else: user_roles = self.controller.roles.get_all_roles() - server = bleach.clean(self.get_argument('server', '')) - server_name = bleach.clean(self.get_argument('server_name', '')) - port = bleach.clean(self.get_argument('port', '')) - import_type = bleach.clean(self.get_argument('create_type', '')) - import_server_path = bleach.clean(self.get_argument('server_path', '')) - import_server_exe = bleach.clean(self.get_argument('server_jar', '')) + server = bleach.clean(self.get_argument("server", "")) + server_name = bleach.clean(self.get_argument("server_name", "")) + port = bleach.clean(self.get_argument("port", "")) + import_type = bleach.clean(self.get_argument("create_type", "")) + import_server_path = bleach.clean(self.get_argument("server_path", "")) + import_server_exe = bleach.clean(self.get_argument("server_jar", "")) server_parts = server.split("|") captured_roles = [] for role in user_roles: - if bleach.clean(self.get_argument(str(role), '')) == "on": + if bleach.clean(self.get_argument(str(role), "")) == "on": captured_roles.append(role) if not server_name: self.redirect("/panel/error?error=Server name cannot be empty!") return - if import_type == 'import_jar': - good_path = self.controller.verify_jar_server(import_server_path, import_server_exe) + if import_type == "import_jar": + good_path = self.controller.verify_jar_server( + import_server_path, import_server_exe + ) if not good_path: - self.redirect("/panel/error?error=Server path or Server Jar not found!") + self.redirect( + "/panel/error?error=Server path or Server Jar not found!" + ) return - new_server_id = self.controller.import_bedrock_server(server_name, import_server_path,import_server_exe, port) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"imported a jar server named \"{server_name}\"", # Example: Admin imported a server named "old creative" - new_server_id, - self.get_remote_ip()) - elif import_type == 'import_zip': + new_server_id = self.controller.import_bedrock_server( + server_name, import_server_path, import_server_exe, port + ) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f'imported a jar server named "{server_name}"', # Example: Admin imported a server named "old creative" + new_server_id, + self.get_remote_ip(), + ) + elif import_type == "import_zip": # here import_server_path means the zip path - zip_path = bleach.clean(self.get_argument('root_path')) + zip_path = bleach.clean(self.get_argument("root_path")) good_path = helper.check_path_exists(zip_path) if not good_path: self.redirect("/panel/error?error=Temp path not found!") return - new_server_id = self.controller.import_bedrock_zip_server(server_name, zip_path, import_server_exe, port) + new_server_id = self.controller.import_bedrock_zip_server( + server_name, zip_path, import_server_exe, port + ) if new_server_id == "false": - self.redirect("/panel/error?error=Zip file not accessible! You can fix this permissions issue with" + - f"sudo chown -R crafty:crafty {import_server_path} And sudo chmod 2775 -R {import_server_path}") + self.redirect( + "/panel/error?error=Zip file not accessible! You can fix this permissions issue with" + + f"sudo chown -R crafty:crafty {import_server_path} And sudo chmod 2775 -R {import_server_path}" + ) return - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"imported a zip server named \"{server_name}\"", # Example: Admin imported a server named "old creative" - new_server_id, - self.get_remote_ip()) - #deletes temp dir + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f'imported a zip server named "{server_name}"', # Example: Admin imported a server named "old creative" + new_server_id, + self.get_remote_ip(), + ) + # deletes temp dir file_helper.del_dirs(zip_path) else: if len(server_parts) != 2: @@ -338,26 +435,42 @@ class ServerHandler(BaseHandler): server_type, server_version = server_parts # TODO: add server type check here and call the correct server add functions if not a jar role_ids = self.controller.users.get_user_roles_id(exec_user["user_id"]) - new_server_id = self.controller.create_jar_server(server_type, server_version, server_name, min_mem, max_mem, port) - self.controller.management.add_to_audit_log(exec_user['user_id'], - f"created a {server_version} {str(server_type).capitalize()} server named \"{server_name}\"", - # Example: Admin created a 1.16.5 Bukkit server named "survival" - new_server_id, - self.get_remote_ip()) + new_server_id = self.controller.create_jar_server( + server_type, server_version, server_name, min_mem, max_mem, port + ) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f'created a {server_version} {str(server_type).capitalize()} server named "{server_name}"', + # Example: Admin created a 1.16.5 Bukkit server named "survival" + new_server_id, + self.get_remote_ip(), + ) # These lines create a new Role for the Server with full permissions and add the user to it if he's not a superuser if len(captured_roles) == 0: if not superuser: - new_server_uuid = self.controller.servers.get_server_data_by_id(new_server_id).get("server_uuid") - role_id = self.controller.roles.add_role(f"Creator of Server with uuid={new_server_uuid}") - self.controller.server_perms.add_role_server(new_server_id, role_id, "11111111") - self.controller.users.add_role_to_user(exec_user["user_id"], role_id) - self.controller.crafty_perms.add_server_creation(exec_user["user_id"]) + new_server_uuid = self.controller.servers.get_server_data_by_id( + new_server_id + ).get("server_uuid") + role_id = self.controller.roles.add_role( + f"Creator of Server with uuid={new_server_uuid}" + ) + self.controller.server_perms.add_role_server( + new_server_id, role_id, "11111111" + ) + self.controller.users.add_role_to_user( + exec_user["user_id"], role_id + ) + self.controller.crafty_perms.add_server_creation( + exec_user["user_id"] + ) else: for role in captured_roles: role_id = role - self.controller.server_perms.add_role_server(new_server_id, role_id, "11111111") + self.controller.server_perms.add_role_server( + new_server_id, role_id, "11111111" + ) self.controller.stats.record_stats() self.redirect("/panel/dashboard") @@ -369,4 +482,4 @@ class ServerHandler(BaseHandler): translate=self.translator.translate, ) except RuntimeError: - self.redirect('/panel/dashboard') + self.redirect("/panel/dashboard") diff --git a/app/classes/web/static_handler.py b/app/classes/web/static_handler.py index 4d401ddb..731b48f4 100644 --- a/app/classes/web/static_handler.py +++ b/app/classes/web/static_handler.py @@ -1,17 +1,24 @@ -from typing import ( Optional ) +from typing import Optional try: import tornado.web except ModuleNotFoundError as e: from app.classes.shared.helpers import helper + helper.auto_installer_fix(e) + class CustomStaticHandler(tornado.web.StaticFileHandler): def validate_absolute_path(self, root: str, absolute_path: str) -> Optional[str]: try: return super().validate_absolute_path(root, absolute_path) except tornado.web.HTTPError as error: - if 'HTTP 404: Not Found' in str(error): + if "HTTP 404: Not Found" in str(error): self.set_status(404) - self.finish({'error':'NOT_FOUND', 'info':'The requested resource was not found on the server'}) + self.finish( + { + "error": "NOT_FOUND", + "info": "The requested resource was not found on the server", + } + ) diff --git a/app/classes/web/status_handler.py b/app/classes/web/status_handler.py index 07a488ca..980df3e4 100644 --- a/app/classes/web/status_handler.py +++ b/app/classes/web/status_handler.py @@ -5,42 +5,48 @@ from app.classes.web.base_handler import BaseHandler logger = logging.getLogger(__name__) + class StatusHandler(BaseHandler): def get(self): page_data = {} - page_data['lang'] = helper.get_setting('language') - page_data['lang_page'] = helper.getLangPage(helper.get_setting('language')) - page_data['servers'] = self.controller.servers.get_all_servers_stats() + page_data["lang"] = helper.get_setting("language") + page_data["lang_page"] = helper.getLangPage(helper.get_setting("language")) + page_data["servers"] = self.controller.servers.get_all_servers_stats() running = 0 - for srv in page_data['servers']: - if srv['stats']['running']: + for srv in page_data["servers"]: + if srv["stats"]["running"]: running += 1 - server_data = srv.get('server_data', False) - server_id = server_data.get('server_id', False) - srv['raw_ping_result'] = self.controller.servers.get_server_stats_by_id(server_id) - if 'icon' not in srv['raw_ping_result']: - srv['raw_ping_result']['icon'] = False + server_data = srv.get("server_data", False) + server_id = server_data.get("server_id", False) + srv["raw_ping_result"] = self.controller.servers.get_server_stats_by_id( + server_id + ) + if "icon" not in srv["raw_ping_result"]: + srv["raw_ping_result"]["icon"] = False - page_data['running'] = running + page_data["running"] = running - template = 'public/status.html' + template = "public/status.html" self.render( - template, - data=page_data, - translate=self.translator.translate, - ) + template, + data=page_data, + translate=self.translator.translate, + ) + def post(self): page_data = {} - page_data['servers'] = self.controller.servers.get_all_servers_stats() - for srv in page_data['servers']: - server_data = srv.get('server_data', False) - server_id = server_data.get('server_id', False) - srv['raw_ping_result'] = self.controller.servers.get_server_stats_by_id(server_id) - template = 'public/status.html' + page_data["servers"] = self.controller.servers.get_all_servers_stats() + for srv in page_data["servers"]: + server_data = srv.get("server_data", False) + server_id = server_data.get("server_id", False) + srv["raw_ping_result"] = self.controller.servers.get_server_stats_by_id( + server_id + ) + template = "public/status.html" self.render( - template, - data=page_data, - translate=self.translator.translate, - ) + template, + data=page_data, + translate=self.translator.translate, + ) diff --git a/app/classes/web/tornado_handler.py b/app/classes/web/tornado_handler.py index ca411751..ad62629c 100644 --- a/app/classes/web/tornado_handler.py +++ b/app/classes/web/tornado_handler.py @@ -34,8 +34,8 @@ except ModuleNotFoundError as e: logger = logging.getLogger(__name__) -class Webserver: +class Webserver: def __init__(self, controller, tasks_manager): self.ioloop = None self.HTTP_Server = None @@ -48,12 +48,12 @@ class Webserver: def log_function(handler): info = { - 'Status_Code': handler.get_status(), - 'Method': handler.request.method, - 'URL': handler.request.uri, - 'Remote_IP': handler.request.remote_ip, + "Status_Code": handler.get_status(), + "Method": handler.request.method, + "URL": handler.request.uri, + "Remote_IP": handler.request.remote_ip, # pylint: disable=consider-using-f-string - 'Elapsed_Time': '%.2fms' % (handler.request.request_time() * 1000) + "Elapsed_Time": "%.2fms" % (handler.request.request_time() * 1000), } tornado.log.access_log.info(json.dumps(info, indent=4)) @@ -72,12 +72,17 @@ class Webserver: if sys.platform.startswith("win") and sys.version_info >= (3, 8): # pylint: disable=reimported,import-outside-toplevel,redefined-outer-name import asyncio + try: from asyncio import WindowsSelectorEventLoopPolicy except ImportError: - logger.debug("asyncio patch isn't required") # Can't assign a policy which doesn't exist. + logger.debug( + "asyncio patch isn't required" + ) # Can't assign a policy which doesn't exist. else: - if not isinstance(asyncio.get_event_loop_policy(), WindowsSelectorEventLoopPolicy): + if not isinstance( + asyncio.get_event_loop_policy(), WindowsSelectorEventLoopPolicy + ): asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy()) logger.debug("Applied asyncio patch") @@ -86,11 +91,11 @@ class Webserver: # let's verify we have an SSL cert helper.create_self_signed_cert() - http_port = helper.get_setting('http_port') - https_port = helper.get_setting('https_port') + http_port = helper.get_setting("http_port") + https_port = helper.get_setting("https_port") - debug_errors = helper.get_setting('show_errors') - cookie_secret = helper.get_setting('cookie_secret') + debug_errors = helper.get_setting("show_errors") + cookie_secret = helper.get_setting("cookie_secret") if cookie_secret is False: cookie_secret = helper.random_string_generator(32) @@ -102,38 +107,46 @@ class Webserver: https_port = 8443 cert_objects = { - 'certfile': os.path.join(helper.config_dir, 'web', 'certs', 'commander.cert.pem'), - 'keyfile': os.path.join(helper.config_dir, 'web', 'certs', 'commander.key.pem'), + "certfile": os.path.join( + helper.config_dir, "web", "certs", "commander.cert.pem" + ), + "keyfile": os.path.join( + helper.config_dir, "web", "certs", "commander.key.pem" + ), } logger.info(f"Starting Web Server on ports http:{http_port} https:{https_port}") asyncio.set_event_loop(asyncio.new_event_loop()) - tornado.template.Loader('.') + tornado.template.Loader(".") # TODO: Remove because we don't and won't use - tornado.locale.set_default_locale('en_EN') + tornado.locale.set_default_locale("en_EN") - handler_args = {"controller": self.controller, "tasks_manager": self.tasks_manager, "translator": translation} + handler_args = { + "controller": self.controller, + "tasks_manager": self.tasks_manager, + "translator": translation, + } handlers = [ - (r'/', DefaultHandler, handler_args), - (r'/public/(.*)', PublicHandler, handler_args), - (r'/panel/(.*)', PanelHandler, handler_args), - (r'/server/(.*)', ServerHandler, handler_args), - (r'/ajax/(.*)', AjaxHandler, handler_args), - (r'/files/(.*)', FileHandler, handler_args), - (r'/api/stats/servers', ServersStats, handler_args), - (r'/api/stats/node', NodeStats, handler_args), - (r'/ws', SocketHandler, handler_args), - (r'/upload', UploadHandler, handler_args), - (r'/status', StatusHandler, handler_args) - ] + (r"/", DefaultHandler, handler_args), + (r"/public/(.*)", PublicHandler, handler_args), + (r"/panel/(.*)", PanelHandler, handler_args), + (r"/server/(.*)", ServerHandler, handler_args), + (r"/ajax/(.*)", AjaxHandler, handler_args), + (r"/files/(.*)", FileHandler, handler_args), + (r"/api/stats/servers", ServersStats, handler_args), + (r"/api/stats/node", NodeStats, handler_args), + (r"/ws", SocketHandler, handler_args), + (r"/upload", UploadHandler, handler_args), + (r"/status", StatusHandler, handler_args), + ] app = tornado.web.Application( handlers, - template_path=os.path.join(helper.webroot, 'templates'), - static_path=os.path.join(helper.webroot, 'static'), + template_path=os.path.join(helper.webroot, "templates"), + static_path=os.path.join(helper.webroot, "static"), debug=debug_errors, cookie_secret=cookie_secret, xsrf_cookies=True, @@ -144,25 +157,27 @@ class Webserver: static_handler_class=CustomStaticHandler, serve_traceback=debug_errors, ) - HTTPhanders = [(r'/', HTTPHandler, handler_args), - (r'/public/(.*)', HTTPHandlerPage, handler_args), - (r'/panel/(.*)', HTTPHandlerPage, handler_args), - (r'/server/(.*)', HTTPHandlerPage, handler_args), - (r'/ajax/(.*)', HTTPHandlerPage, handler_args), - (r'/api/stats/servers', HTTPHandlerPage, handler_args), - (r'/api/stats/node', HTTPHandlerPage, handler_args), - (r'/ws', HTTPHandlerPage, handler_args), - (r'/upload', HTTPHandlerPage, handler_args)] + HTTPhanders = [ + (r"/", HTTPHandler, handler_args), + (r"/public/(.*)", HTTPHandlerPage, handler_args), + (r"/panel/(.*)", HTTPHandlerPage, handler_args), + (r"/server/(.*)", HTTPHandlerPage, handler_args), + (r"/ajax/(.*)", HTTPHandlerPage, handler_args), + (r"/api/stats/servers", HTTPHandlerPage, handler_args), + (r"/api/stats/node", HTTPHandlerPage, handler_args), + (r"/ws", HTTPHandlerPage, handler_args), + (r"/upload", HTTPHandlerPage, handler_args), + ] HTTPapp = tornado.web.Application( HTTPhanders, - template_path=os.path.join(helper.webroot, 'templates'), - static_path=os.path.join(helper.webroot, 'static'), + template_path=os.path.join(helper.webroot, "templates"), + static_path=os.path.join(helper.webroot, "static"), debug=debug_errors, cookie_secret=cookie_secret, xsrf_cookies=True, autoreload=False, log_function=self.log_function, - default_handler_class = HTTPHandler, + default_handler_class=HTTPHandler, login_url="/login", serve_traceback=debug_errors, ) @@ -173,8 +188,12 @@ class Webserver: self.HTTPS_Server = tornado.httpserver.HTTPServer(app, ssl_options=cert_objects) self.HTTPS_Server.listen(https_port) - logger.info(f"https://{helper.get_local_ip()}:{https_port} is up and ready for connections.") - console.info(f"https://{helper.get_local_ip()}:{https_port} is up and ready for connections.") + logger.info( + f"https://{helper.get_local_ip()}:{https_port} is up and ready for connections." + ) + console.info( + f"https://{helper.get_local_ip()}:{https_port} is up and ready for connections." + ) console.info("Server Init Complete: Listening For Connections:") diff --git a/app/classes/web/upload_handler.py b/app/classes/web/upload_handler.py index 2702593e..9a6e71a0 100644 --- a/app/classes/web/upload_handler.py +++ b/app/classes/web/upload_handler.py @@ -22,11 +22,14 @@ logger = logging.getLogger(__name__) # Class & Function Defination MAX_STREAMED_SIZE = 1024 * 1024 * 1024 + @tornado.web.stream_request_body class UploadHandler(BaseHandler): # noinspection PyAttributeOutsideInit - def initialize(self, controller: Controller=None, tasks_manager=None, translator=None): + def initialize( + self, controller: Controller = None, tasks_manager=None, translator=None + ): self.controller = controller self.tasks_manager = tasks_manager self.translator = translator @@ -35,45 +38,74 @@ class UploadHandler(BaseHandler): self.do_upload = True # pylint: disable=unused-variable api_key, token_data, exec_user = self.current_user - server_id = self.get_argument('server_id', None) - superuser = exec_user['superuser'] + server_id = self.get_argument("server_id", None) + superuser = exec_user["superuser"] if api_key is not None: superuser = superuser and api_key.superuser - user_id = exec_user['user_id'] + user_id = exec_user["user_id"] if superuser: - exec_user_server_permissions = self.controller.server_perms.list_defined_permissions() + exec_user_server_permissions = ( + self.controller.server_perms.list_defined_permissions() + ) elif api_key is not None: - exec_user_server_permissions = self.controller.server_perms.get_api_key_permissions_list(api_key, server_id) + exec_user_server_permissions = ( + self.controller.server_perms.get_api_key_permissions_list( + api_key, server_id + ) + ) else: - exec_user_server_permissions = self.controller.server_perms.get_user_id_permissions_list( - exec_user["user_id"], server_id) + exec_user_server_permissions = ( + self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) + ) - server_id = self.request.headers.get('X-ServerId', None) + server_id = self.request.headers.get("X-ServerId", None) if user_id is None: - logger.warning('User ID not found in upload handler call') - console.warning('User ID not found in upload handler call') + logger.warning("User ID not found in upload handler call") + console.warning("User ID not found in upload handler call") self.do_upload = False if server_id is None: - logger.warning('Server ID not found in upload handler call') - console.warning('Server ID not found in upload handler call') + logger.warning("Server ID not found in upload handler call") + console.warning("Server ID not found in upload handler call") self.do_upload = False if Enum_Permissions_Server.Files not in exec_user_server_permissions: - logger.warning(f'User {user_id} tried to upload a file to {server_id} without permissions!') - console.warning(f'User {user_id} tried to upload a file to {server_id} without permissions!') + logger.warning( + f"User {user_id} tried to upload a file to {server_id} without permissions!" + ) + console.warning( + f"User {user_id} tried to upload a file to {server_id} without permissions!" + ) self.do_upload = False - path = self.request.headers.get('X-Path', None) - filename = self.request.headers.get('X-FileName', None) + path = self.request.headers.get("X-Path", None) + filename = self.request.headers.get("X-FileName", None) full_path = os.path.join(path, filename) - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), full_path): - print(user_id, server_id, helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), full_path) - logger.warning(f'User {user_id} tried to upload a file to {server_id} but the path is not inside of the server!') - console.warning(f'User {user_id} tried to upload a file to {server_id} but the path is not inside of the server!') + if not helper.in_path( + helper.get_os_understandable_path( + self.controller.servers.get_server_data_by_id(server_id)["path"] + ), + full_path, + ): + print( + user_id, + server_id, + helper.get_os_understandable_path( + self.controller.servers.get_server_data_by_id(server_id)["path"] + ), + full_path, + ) + logger.warning( + f"User {user_id} tried to upload a file to {server_id} but the path is not inside of the server!" + ) + console.warning( + f"User {user_id} tried to upload a file to {server_id} but the path is not inside of the server!" + ) self.do_upload = False if self.do_upload: @@ -87,19 +119,19 @@ class UploadHandler(BaseHandler): def post(self): logger.info("Upload completed") - files_left = int(self.request.headers.get('X-Files-Left', None)) + files_left = int(self.request.headers.get("X-Files-Left", None)) if self.do_upload: time.sleep(5) if files_left == 0: - websocket_helper.broadcast('close_upload_box', 'success') - self.finish('success') # Nope, I'm sending "success" + websocket_helper.broadcast("close_upload_box", "success") + self.finish("success") # Nope, I'm sending "success" self.f.close() else: time.sleep(5) if files_left == 0: - websocket_helper.broadcast('close_upload_box', 'error') - self.finish('error') + websocket_helper.broadcast("close_upload_box", "error") + self.finish("error") def data_received(self, chunk): if self.do_upload: diff --git a/app/classes/web/websocket_handler.py b/app/classes/web/websocket_handler.py index d8146fd3..ea583b96 100644 --- a/app/classes/web/websocket_handler.py +++ b/app/classes/web/websocket_handler.py @@ -15,6 +15,7 @@ except ModuleNotFoundError as e: logger = logging.getLogger(__name__) + class SocketHandler(tornado.websocket.WebSocketHandler): page = None page_query_params = None @@ -30,56 +31,71 @@ class SocketHandler(tornado.websocket.WebSocketHandler): self.io_loop = tornado.ioloop.IOLoop.current() def get_remote_ip(self): - remote_ip = self.request.headers.get("X-Real-IP") or \ - self.request.headers.get("X-Forwarded-For") or \ - self.request.remote_ip + remote_ip = ( + self.request.headers.get("X-Real-IP") + or self.request.headers.get("X-Forwarded-For") + or self.request.remote_ip + ) return remote_ip def get_user_id(self): - _, _, user = authentication.check(self.get_cookie('token')) - return user['user_id'] + _, _, user = authentication.check(self.get_cookie("token")) + return user["user_id"] def check_auth(self): - return authentication.check_bool(self.get_cookie('token')) + return authentication.check_bool(self.get_cookie("token")) # pylint: disable=arguments-differ def open(self): - logger.debug('Checking WebSocket authentication') + logger.debug("Checking WebSocket authentication") if self.check_auth(): self.handle() else: - websocket_helper.send_message(self, 'notification', 'Not authenticated for WebSocket connection') + websocket_helper.send_message( + self, "notification", "Not authenticated for WebSocket connection" + ) self.close() - self.controller.management.add_to_audit_log_raw('unknown', - 0, 0, - 'Someone tried to connect via WebSocket without proper authentication', - self.get_remote_ip()) - websocket_helper.broadcast('notification', 'Someone tried to connect via WebSocket without proper authentication') - logger.warning('Someone tried to connect via WebSocket without proper authentication') + self.controller.management.add_to_audit_log_raw( + "unknown", + 0, + 0, + "Someone tried to connect via WebSocket without proper authentication", + self.get_remote_ip(), + ) + websocket_helper.broadcast( + "notification", + "Someone tried to connect via WebSocket without proper authentication", + ) + logger.warning( + "Someone tried to connect via WebSocket without proper authentication" + ) def handle(self): - self.page = self.get_query_argument('page') - self.page_query_params = dict(parse_qsl(helper.remove_prefix( - self.get_query_argument('page_query_params'), - '?' - ))) + self.page = self.get_query_argument("page") + self.page_query_params = dict( + parse_qsl( + helper.remove_prefix(self.get_query_argument("page_query_params"), "?") + ) + ) websocket_helper.add_client(self) - logger.debug('Opened WebSocket connection') + logger.debug("Opened WebSocket connection") # pylint: disable=arguments-renamed @staticmethod def on_message(raw_message): - logger.debug(f'Got message from WebSocket connection {raw_message}') + logger.debug(f"Got message from WebSocket connection {raw_message}") message = json.loads(raw_message) logger.debug(f"Event Type: {message['event']}, Data: {message['data']}") def on_close(self): websocket_helper.remove_client(self) - logger.debug('Closed WebSocket connection') + logger.debug("Closed WebSocket connection") async def write_message_int(self, message): self.write_message(message) def write_message_helper(self, message): - asyncio.run_coroutine_threadsafe(self.write_message_int(message), self.io_loop.asyncio_loop) + asyncio.run_coroutine_threadsafe( + self.write_message_int(message), self.io_loop.asyncio_loop + ) diff --git a/app/classes/web/websocket_helper.py b/app/classes/web/websocket_helper.py index c124e351..9d39659b 100644 --- a/app/classes/web/websocket_helper.py +++ b/app/classes/web/websocket_helper.py @@ -5,6 +5,7 @@ from app.classes.shared.console import console logger = logging.getLogger(__name__) + class WebSocketHelper: def __init__(self): self.clients = set() @@ -18,16 +19,20 @@ class WebSocketHelper: # pylint: disable=no-self-use def send_message(self, client, event_type: str, data): if client.check_auth(): - message = str(json.dumps({'event': event_type, 'data': data})) + message = str(json.dumps({"event": event_type, "data": data})) client.write_message_helper(message) def broadcast(self, event_type: str, data): - logger.debug(f"Sending to {len(self.clients)} clients: {json.dumps({'event': event_type, 'data': data})}") + logger.debug( + f"Sending to {len(self.clients)} clients: {json.dumps({'event': event_type, 'data': data})}" + ) for client in self.clients: try: self.send_message(client, event_type, data) except Exception as e: - logger.exception(f'Error caught while sending WebSocket message to {client.get_remote_ip()} {e}') + logger.exception( + f"Error caught while sending WebSocket message to {client.get_remote_ip()} {e}" + ) def broadcast_page(self, page: str, event_type: str, data): def filter_fn(client): @@ -51,7 +56,9 @@ class WebSocketHelper: self.broadcast_with_fn(filter_fn, event_type, data) - def broadcast_user_page_params(self, page: str, params: dict, user_id: str, event_type: str, data): + def broadcast_user_page_params( + self, page: str, params: dict, user_id: str, event_type: str, data + ): def filter_fn(client): if client.get_user_id() != user_id: return False @@ -77,18 +84,23 @@ class WebSocketHelper: def broadcast_with_fn(self, filter_fn, event_type: str, data): clients = list(filter(filter_fn, self.clients)) - logger.debug(f"Sending to {len(clients)} out of {len(self.clients)} clients: {json.dumps({'event': event_type, 'data': data})}") + logger.debug( + f"Sending to {len(clients)} out of {len(self.clients)} clients: {json.dumps({'event': event_type, 'data': data})}" + ) for client in clients: try: self.send_message(client, event_type, data) except Exception as e: - logger.exception(f'Error catched while sending WebSocket message to {client.get_remote_ip()} {e}') + logger.exception( + f"Error catched while sending WebSocket message to {client.get_remote_ip()} {e}" + ) def disconnect_all(self): - console.info('Disconnecting WebSocket clients') + console.info("Disconnecting WebSocket clients") for client in self.clients: client.close() - console.info('Disconnected WebSocket clients') + console.info("Disconnected WebSocket clients") + websocket_helper = WebSocketHelper() diff --git a/app/migrations/20210813111015_init.py b/app/migrations/20210813111015_init.py index 66e7c83a..1b571f21 100644 --- a/app/migrations/20210813111015_init.py +++ b/app/migrations/20210813111015_init.py @@ -4,6 +4,7 @@ import datetime def migrate(migrator, database, **kwargs): db = database + class Users(peewee.Model): user_id = peewee.AutoField() created = peewee.DateTimeField(default=datetime.datetime.now) @@ -32,12 +33,12 @@ def migrate(migrator, database, **kwargs): database = db class User_Roles(peewee.Model): - user_id = peewee.ForeignKeyField(Users, backref='user_role') - role_id = peewee.ForeignKeyField(Roles, backref='user_role') + user_id = peewee.ForeignKeyField(Users, backref="user_role") + role_id = peewee.ForeignKeyField(Roles, backref="user_role") class Meta: - table_name = 'user_roles' - primary_key = peewee.CompositeKey('user_id', 'role_id') + table_name = "user_roles" + primary_key = peewee.CompositeKey("user_id", "role_id") database = db class Audit_Log(peewee.Model): @@ -45,10 +46,10 @@ def migrate(migrator, database, **kwargs): created = peewee.DateTimeField(default=datetime.datetime.now) user_name = peewee.CharField(default="") user_id = peewee.IntegerField(default=0, index=True) - source_ip = peewee.CharField(default='127.0.0.1') + source_ip = peewee.CharField(default="127.0.0.1") # When auditing global events, use server ID 0 server_id = peewee.IntegerField(default=None, index=True) - log_msg = peewee.TextField(default='') + log_msg = peewee.TextField(default="") class Meta: database = db @@ -93,27 +94,27 @@ def migrate(migrator, database, **kwargs): database = db class User_Servers(peewee.Model): - user_id = peewee.ForeignKeyField(Users, backref='user_server') - server_id = peewee.ForeignKeyField(Servers, backref='user_server') + user_id = peewee.ForeignKeyField(Users, backref="user_server") + server_id = peewee.ForeignKeyField(Servers, backref="user_server") class Meta: - table_name = 'user_servers' - primary_key = peewee.CompositeKey('user_id', 'server_id') + table_name = "user_servers" + primary_key = peewee.CompositeKey("user_id", "server_id") database = db class Role_Servers(peewee.Model): - role_id = peewee.ForeignKeyField(Roles, backref='role_server') - server_id = peewee.ForeignKeyField(Servers, backref='role_server') + role_id = peewee.ForeignKeyField(Roles, backref="role_server") + server_id = peewee.ForeignKeyField(Servers, backref="role_server") class Meta: - table_name = 'role_servers' - primary_key = peewee.CompositeKey('role_id', 'server_id') + table_name = "role_servers" + primary_key = peewee.CompositeKey("role_id", "server_id") database = db class Server_Stats(peewee.Model): stats_id = peewee.AutoField() created = peewee.DateTimeField(default=datetime.datetime.now) - server_id = peewee.ForeignKeyField(Servers, backref='server', index=True) + server_id = peewee.ForeignKeyField(Servers, backref="server", index=True) started = peewee.CharField(default="") running = peewee.BooleanField(default=False) cpu = peewee.FloatField(default=0) @@ -137,10 +138,10 @@ def migrate(migrator, database, **kwargs): class Commands(peewee.Model): command_id = peewee.AutoField() created = peewee.DateTimeField(default=datetime.datetime.now) - server_id = peewee.ForeignKeyField(Servers, backref='server', index=True) - user = peewee.ForeignKeyField(Users, backref='user', index=True) - source_ip = peewee.CharField(default='127.0.0.1') - command = peewee.CharField(default='') + server_id = peewee.ForeignKeyField(Servers, backref="server", index=True) + user = peewee.ForeignKeyField(Users, backref="user", index=True) + source_ip = peewee.CharField(default="127.0.0.1") + command = peewee.CharField(default="") executed = peewee.BooleanField(default=False) class Meta: @@ -161,7 +162,7 @@ def migrate(migrator, database, **kwargs): class Schedules(peewee.Model): schedule_id = peewee.IntegerField(unique=True, primary_key=True) - server_id = peewee.ForeignKeyField(Servers, backref='schedule_server') + server_id = peewee.ForeignKeyField(Servers, backref="schedule_server") enabled = peewee.BooleanField() action = peewee.CharField() interval = peewee.IntegerField() @@ -171,17 +172,17 @@ def migrate(migrator, database, **kwargs): comment = peewee.CharField() class Meta: - table_name = 'schedules' + table_name = "schedules" database = db class Backups(peewee.Model): directories = peewee.CharField(null=True) max_backups = peewee.IntegerField() - server_id = peewee.ForeignKeyField(Servers, backref='backups_server') - schedule_id = peewee.ForeignKeyField(Schedules, backref='backups_schedule') + server_id = peewee.ForeignKeyField(Servers, backref="backups_server") + schedule_id = peewee.ForeignKeyField(Schedules, backref="backups_schedule") class Meta: - table_name = 'backups' + table_name = "backups" database = db migrator.create_table(Backups) @@ -200,16 +201,18 @@ def migrate(migrator, database, **kwargs): def rollback(migrator, database, **kwargs): - migrator.drop_table('users') - migrator.drop_table('roles') - migrator.drop_table('user_roles') - migrator.drop_table('audit_log') # ? Not 100% sure of the table name, please specify in the schema - migrator.drop_table('host_stats') - migrator.drop_table('servers') - migrator.drop_table('user_servers') - migrator.drop_table('role_servers') - migrator.drop_table('server_stats') - migrator.drop_table('commands') - migrator.drop_table('webhooks') - migrator.drop_table('schedules') - migrator.drop_table('backups') + migrator.drop_table("users") + migrator.drop_table("roles") + migrator.drop_table("user_roles") + migrator.drop_table( + "audit_log" + ) # ? Not 100% sure of the table name, please specify in the schema + migrator.drop_table("host_stats") + migrator.drop_table("servers") + migrator.drop_table("user_servers") + migrator.drop_table("role_servers") + migrator.drop_table("server_stats") + migrator.drop_table("commands") + migrator.drop_table("webhooks") + migrator.drop_table("schedules") + migrator.drop_table("backups") diff --git a/app/migrations/20210819155737_permissions.py b/app/migrations/20210819155737_permissions.py index d6815548..ba42677d 100644 --- a/app/migrations/20210819155737_permissions.py +++ b/app/migrations/20210819155737_permissions.py @@ -1,17 +1,26 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('user_servers', permissions=peewee.CharField(default='00000000')) # First argument can be model class OR table name - migrator.add_columns('role_servers', permissions=peewee.CharField(default='00000000')) # First argument can be model class OR table name + migrator.add_columns( + "user_servers", permissions=peewee.CharField(default="00000000") + ) # First argument can be model class OR table name + migrator.add_columns( + "role_servers", permissions=peewee.CharField(default="00000000") + ) # First argument can be model class OR table name """ Write your migrations here. """ def rollback(migrator, database, **kwargs): - migrator.drop_columns('user_servers', ['permissions']) # First argument can be model class OR table name - migrator.drop_columns('role_servers', ['permissions']) # First argument can be model class OR table name + migrator.drop_columns( + "user_servers", ["permissions"] + ) # First argument can be model class OR table name + migrator.drop_columns( + "role_servers", ["permissions"] + ) # First argument can be model class OR table name """ Write your rollback migrations here. """ diff --git a/app/migrations/20210822092240_crafty_permissions.py b/app/migrations/20210822092240_crafty_permissions.py index e39884fa..b9172aaa 100644 --- a/app/migrations/20210822092240_crafty_permissions.py +++ b/app/migrations/20210822092240_crafty_permissions.py @@ -2,25 +2,27 @@ from peewee import * from app.classes.models.users import Users + def migrate(migrator, database, **kwargs): db = database + class User_Crafty(Model): - user_id = ForeignKeyField(Users, backref='users_crafty') + user_id = ForeignKeyField(Users, backref="users_crafty") permissions = CharField(default="00000000") limit_server_creation = IntegerField(default=-1) class Meta: - table_name = 'user_crafty' + table_name = "user_crafty" database = db + migrator.create_table(User_Crafty) """ Write your migrations here. """ - -def rollback(migrator, database, **kwargs): - migrator.drop_table('user_crafty') # Can be model class OR table name +def rollback(migrator, database, **kwargs): + migrator.drop_table("user_crafty") # Can be model class OR table name """ Write your rollback migrations here. diff --git a/app/migrations/20210822101530_delete_User_Servers.py b/app/migrations/20210822101530_delete_User_Servers.py index efe2a3a9..98ccca68 100644 --- a/app/migrations/20210822101530_delete_User_Servers.py +++ b/app/migrations/20210822101530_delete_User_Servers.py @@ -5,24 +5,25 @@ from app.classes.models.servers import Servers def migrate(migrator, database, **kwargs): - migrator.drop_table('user_servers') # Can be model class OR table name + migrator.drop_table("user_servers") # Can be model class OR table name """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - db = database + db = database + class User_Servers(Model): - user_id = ForeignKeyField(Users, backref='user_server') - server_id = ForeignKeyField(Servers, backref='user_server') + user_id = ForeignKeyField(Users, backref="user_server") + server_id = ForeignKeyField(Servers, backref="user_server") permissions = CharField(default="00000000") class Meta: - table_name = 'user_servers' - primary_key = CompositeKey('user_id', 'server_id') + table_name = "user_servers" + primary_key = CompositeKey("user_id", "server_id") database = db + migrator.create_table(User_Servers) """ Write your rollback migrations here. diff --git a/app/migrations/20210824205501_permissions_limits_1.py b/app/migrations/20210824205501_permissions_limits_1.py index 73ee8d29..81bb21ab 100644 --- a/app/migrations/20210824205501_permissions_limits_1.py +++ b/app/migrations/20210824205501_permissions_limits_1.py @@ -1,24 +1,28 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('user_crafty', limit_user_creation=peewee.IntegerField(default=0)) - migrator.add_columns('user_crafty', limit_role_creation=peewee.IntegerField(default=0)) - migrator.add_columns('user_crafty', created_server=peewee.IntegerField(default=0)) - migrator.add_columns('user_crafty', created_user=peewee.IntegerField(default=0)) - migrator.add_columns('user_crafty', created_role=peewee.IntegerField(default=0)) + migrator.add_columns( + "user_crafty", limit_user_creation=peewee.IntegerField(default=0) + ) + migrator.add_columns( + "user_crafty", limit_role_creation=peewee.IntegerField(default=0) + ) + migrator.add_columns("user_crafty", created_server=peewee.IntegerField(default=0)) + migrator.add_columns("user_crafty", created_user=peewee.IntegerField(default=0)) + migrator.add_columns("user_crafty", created_role=peewee.IntegerField(default=0)) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('user_crafty', ['limit_user_creation']) - migrator.drop_columns('user_crafty', ['limit_role_creation']) - migrator.drop_columns('user_crafty', ['created_server']) - migrator.drop_columns('user_crafty', ['created_user']) - migrator.drop_columns('user_crafty', ['created_role']) + migrator.drop_columns("user_crafty", ["limit_user_creation"]) + migrator.drop_columns("user_crafty", ["limit_role_creation"]) + migrator.drop_columns("user_crafty", ["created_server"]) + migrator.drop_columns("user_crafty", ["created_user"]) + migrator.drop_columns("user_crafty", ["created_role"]) """ Write your rollback migrations here. """ diff --git a/app/migrations/20210915205501_backup_schedule.py b/app/migrations/20210915205501_backup_schedule.py index 09ad6e50..f596ee57 100644 --- a/app/migrations/20210915205501_backup_schedule.py +++ b/app/migrations/20210915205501_backup_schedule.py @@ -2,16 +2,19 @@ import peewee from app.classes.models.management import Schedules + def migrate(migrator, database, **kwargs): - migrator.drop_columns('backups', ['schedule_id']) + migrator.drop_columns("backups", ["schedule_id"]) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.add_columns('backups', schedule_id=peewee.ForeignKeyField(Schedules, backref='backups_schedule')) + migrator.add_columns( + "backups", + schedule_id=peewee.ForeignKeyField(Schedules, backref="backups_schedule"), + ) """ Write your rollback migrations here. """ diff --git a/app/migrations/20210915205501_crash.py b/app/migrations/20210915205501_crash.py index 74937251..fee535e5 100644 --- a/app/migrations/20210915205501_crash.py +++ b/app/migrations/20210915205501_crash.py @@ -1,16 +1,16 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('server_stats', crashed=peewee.BooleanField(default=False)) + migrator.add_columns("server_stats", crashed=peewee.BooleanField(default=False)) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('server_stats', ['crashed']) + migrator.drop_columns("server_stats", ["crashed"]) """ Write your rollback migrations here. """ diff --git a/app/migrations/20210915205501_cron_task.py b/app/migrations/20210915205501_cron_task.py index 62767240..f6e7f293 100644 --- a/app/migrations/20210915205501_cron_task.py +++ b/app/migrations/20210915205501_cron_task.py @@ -1,16 +1,16 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('schedules', cron_string=peewee.CharField(default="")) + migrator.add_columns("schedules", cron_string=peewee.CharField(default="")) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('schedules', ['cron_string']) + migrator.drop_columns("schedules", ["cron_string"]) """ Write your rollback migrations here. """ diff --git a/app/migrations/20210915205501_first_run_.py b/app/migrations/20210915205501_first_run_.py index 73f29e69..5743d1ee 100644 --- a/app/migrations/20210915205501_first_run_.py +++ b/app/migrations/20210915205501_first_run_.py @@ -1,16 +1,16 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('server_stats', first_run=peewee.BooleanField(default=True)) + migrator.add_columns("server_stats", first_run=peewee.BooleanField(default=True)) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('server_stats', ['first_run']) + migrator.drop_columns("server_stats", ["first_run"]) """ Write your rollback migrations here. """ diff --git a/app/migrations/20210915205501_one_time_task.py b/app/migrations/20210915205501_one_time_task.py index 3ed0041a..ccf58961 100644 --- a/app/migrations/20210915205501_one_time_task.py +++ b/app/migrations/20210915205501_one_time_task.py @@ -1,16 +1,16 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('schedules', one_time=peewee.BooleanField(default=False)) + migrator.add_columns("schedules", one_time=peewee.BooleanField(default=False)) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('schedules', ['one_time']) + migrator.drop_columns("schedules", ["one_time"]) """ Write your rollback migrations here. """ diff --git a/app/migrations/20210915205501_server_type.py b/app/migrations/20210915205501_server_type.py index dfcf36aa..29507a4f 100644 --- a/app/migrations/20210915205501_server_type.py +++ b/app/migrations/20210915205501_server_type.py @@ -1,16 +1,16 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('servers', type=peewee.CharField(default="minecraft-java")) + migrator.add_columns("servers", type=peewee.CharField(default="minecraft-java")) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('servers', ['type']) + migrator.drop_columns("servers", ["type"]) """ Write your rollback migrations here. """ diff --git a/app/migrations/20210915205501_user_email.py b/app/migrations/20210915205501_user_email.py index 53727bed..fb37628b 100644 --- a/app/migrations/20210915205501_user_email.py +++ b/app/migrations/20210915205501_user_email.py @@ -1,16 +1,16 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('users', email=peewee.CharField(default="default@example.com")) + migrator.add_columns("users", email=peewee.CharField(default="default@example.com")) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('users', ['email']) + migrator.drop_columns("users", ["email"]) """ Write your rollback migrations here. """ diff --git a/app/migrations/20210915205501_users_log_path.py b/app/migrations/20210915205501_users_log_path.py index c79ad322..de89a82d 100644 --- a/app/migrations/20210915205501_users_log_path.py +++ b/app/migrations/20210915205501_users_log_path.py @@ -1,16 +1,16 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('users', support_logs=peewee.CharField(default="")) + migrator.add_columns("users", support_logs=peewee.CharField(default="")) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('users', ['support_logs']) + migrator.drop_columns("users", ["support_logs"]) """ Write your rollback migrations here. """ diff --git a/app/migrations/20210915205501_waiting_start_1.py b/app/migrations/20210915205501_waiting_start_1.py index 00928284..a95be392 100644 --- a/app/migrations/20210915205501_waiting_start_1.py +++ b/app/migrations/20210915205501_waiting_start_1.py @@ -1,16 +1,18 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('server_stats', waiting_start=peewee.BooleanField(default=False)) + migrator.add_columns( + "server_stats", waiting_start=peewee.BooleanField(default=False) + ) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('server_stats', ['waiting_start']) + migrator.drop_columns("server_stats", ["waiting_start"]) """ Write your rollback migrations here. """ diff --git a/app/migrations/20210929205501_user_lang.py b/app/migrations/20210929205501_user_lang.py index 31ce79bd..28c3887c 100644 --- a/app/migrations/20210929205501_user_lang.py +++ b/app/migrations/20210929205501_user_lang.py @@ -1,16 +1,16 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('users', lang=peewee.CharField(default='en_EN')) + migrator.add_columns("users", lang=peewee.CharField(default="en_EN")) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('users', ['lang']) + migrator.drop_columns("users", ["lang"]) """ Write your rollback migrations here. """ diff --git a/app/migrations/20211120221511_api_keys.py b/app/migrations/20211120221511_api_keys.py index cfa957d2..bede2c92 100644 --- a/app/migrations/20211120221511_api_keys.py +++ b/app/migrations/20211120221511_api_keys.py @@ -3,10 +3,14 @@ import datetime def migrate(migrator, database, **kwargs): - migrator.add_columns('users', valid_tokens_from=peewee.DateTimeField(default=datetime.datetime.now)) - migrator.drop_columns('users', ['api_token']) + migrator.add_columns( + "users", valid_tokens_from=peewee.DateTimeField(default=datetime.datetime.now) + ) + migrator.drop_columns("users", ["api_token"]) def rollback(migrator, database, **kwargs): - migrator.drop_columns('users', ['valid_tokens_from']) - migrator.add_columns('users', api_token=peewee.CharField(default="", unique=True, index=True)) + migrator.drop_columns("users", ["valid_tokens_from"]) + migrator.add_columns( + "users", api_token=peewee.CharField(default="", unique=True, index=True) + ) diff --git a/app/migrations/20211121233959_multi_api_keys.py b/app/migrations/20211121233959_multi_api_keys.py index 51805d0c..a0d9be0a 100644 --- a/app/migrations/20211121233959_multi_api_keys.py +++ b/app/migrations/20211121233959_multi_api_keys.py @@ -6,18 +6,18 @@ from app.classes.models.users import Users def migrate(migrator, db): class ApiKeys(peewee.Model): token_id = peewee.AutoField() - name = peewee.CharField(default='', unique=True, index=True) + name = peewee.CharField(default="", unique=True, index=True) created = peewee.DateTimeField(default=datetime.datetime.now) - user = peewee.ForeignKeyField(Users, backref='api_token', index=True) - server_permissions = peewee.CharField(default='00000000') - crafty_permissions = peewee.CharField(default='000') + user = peewee.ForeignKeyField(Users, backref="api_token", index=True) + server_permissions = peewee.CharField(default="00000000") + crafty_permissions = peewee.CharField(default="000") superuser = peewee.BooleanField(default=False) class Meta: - table_name = 'api_keys' + table_name = "api_keys" migrator.create_table(ApiKeys) def rollback(migrator, db): - migrator.drop_table('api_keys') + migrator.drop_table("api_keys") diff --git a/app/migrations/20220223_schedule_reaction.py b/app/migrations/20220223_schedule_reaction.py index 13655395..cba52210 100644 --- a/app/migrations/20220223_schedule_reaction.py +++ b/app/migrations/20220223_schedule_reaction.py @@ -1,18 +1,18 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('schedules', parent=peewee.IntegerField(null=True)) - migrator.add_columns('schedules', delay=peewee.IntegerField(default=0)) + migrator.add_columns("schedules", parent=peewee.IntegerField(null=True)) + migrator.add_columns("schedules", delay=peewee.IntegerField(default=0)) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('schedules', ['parent']) - migrator.drop_columns('schedules', ['delay']) + migrator.drop_columns("schedules", ["parent"]) + migrator.drop_columns("schedules", ["delay"]) """ Write your rollback migrations here. """ diff --git a/app/migrations/20220226_server_order.py b/app/migrations/20220226_server_order.py index 74351e55..db2ff8b3 100644 --- a/app/migrations/20220226_server_order.py +++ b/app/migrations/20220226_server_order.py @@ -1,16 +1,16 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('users', server_order=peewee.CharField(default='')) + migrator.add_columns("users", server_order=peewee.CharField(default="")) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('users', ['server_order']) + migrator.drop_columns("users", ["server_order"]) """ Write your rollback migrations here. """ diff --git a/app/migrations/20220227162446_backup_options.py b/app/migrations/20220227162446_backup_options.py index 1239dd54..db7142b2 100644 --- a/app/migrations/20220227162446_backup_options.py +++ b/app/migrations/20220227162446_backup_options.py @@ -1,8 +1,10 @@ # Generated by database migrator import peewee + def migrate(migrator, db): - migrator.rename_column('backups', 'directories', 'excluded_dirs') + migrator.rename_column("backups", "directories", "excluded_dirs") + def rollback(migrator, db): - migrator.rename_column('backups', 'excluded_dirs', 'directories') \ No newline at end of file + migrator.rename_column("backups", "excluded_dirs", "directories") diff --git a/app/migrations/20220302_compress_backups.py b/app/migrations/20220302_compress_backups.py index 35f16f73..1f7ab200 100644 --- a/app/migrations/20220302_compress_backups.py +++ b/app/migrations/20220302_compress_backups.py @@ -1,16 +1,16 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('backups', compress=peewee.BooleanField(default=False)) + migrator.add_columns("backups", compress=peewee.BooleanField(default=False)) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('backups', ['compress']) + migrator.drop_columns("backups", ["compress"]) """ Write your rollback migrations here. """ diff --git a/app/migrations/20220303_downloading.py b/app/migrations/20220303_downloading.py index 276012b0..ea5c2691 100644 --- a/app/migrations/20220303_downloading.py +++ b/app/migrations/20220303_downloading.py @@ -1,16 +1,16 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('server_stats', downloading=peewee.BooleanField(default=False)) + migrator.add_columns("server_stats", downloading=peewee.BooleanField(default=False)) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('server_stats', ['downloading']) + migrator.drop_columns("server_stats", ["downloading"]) """ Write your rollback migrations here. """ diff --git a/app/migrations/20220312_support_log_status.py b/app/migrations/20220312_support_log_status.py index c843007b..d6dfeb32 100644 --- a/app/migrations/20220312_support_log_status.py +++ b/app/migrations/20220312_support_log_status.py @@ -1,16 +1,16 @@ # Generated by database migrator import peewee + def migrate(migrator, database, **kwargs): - migrator.add_columns('users', preparing=peewee.BooleanField(default=False)) + migrator.add_columns("users", preparing=peewee.BooleanField(default=False)) """ Write your migrations here. """ - def rollback(migrator, database, **kwargs): - migrator.drop_columns('users', ['preparing']) + migrator.drop_columns("users", ["preparing"]) """ Write your rollback migrations here. """ diff --git a/main.py b/main.py index cd4a9a65..41ea9f53 100644 --- a/main.py +++ b/main.py @@ -7,8 +7,11 @@ import logging.config import signal from app.classes.shared.console import console from app.classes.shared.helpers import helper + if helper.checkRoot(): - console.critical("Root detected. Root/Admin access denied. Run Crafty again with non-elevated permissions.") + console.critical( + "Root detected. Root/Admin access denied. Run Crafty again with non-elevated permissions." + ) time.sleep(5) console.critical("Crafty shutting down. Root/Admin access denied.") sys.exit(0) @@ -39,18 +42,14 @@ def do_intro(): def setup_logging(debug=True): - logging_config_file = os.path.join(os.path.curdir, - 'app', - 'config', - 'logging.json' - ) + logging_config_file = os.path.join(os.path.curdir, "app", "config", "logging.json") if os.path.exists(logging_config_file): # open our logging config file - with open(logging_config_file, 'rt', encoding='utf-8') as f: + with open(logging_config_file, "rt", encoding="utf-8") as f: logging_config = json.load(f) if debug: - logging_config['loggers']['']['level'] = 'DEBUG' + logging_config["loggers"][""]["level"] = "DEBUG" logging.config.dictConfig(logging_config) @@ -61,23 +60,23 @@ def setup_logging(debug=True): # Our Main Starter -if __name__ == '__main__': +if __name__ == "__main__": parser = argparse.ArgumentParser("Crafty Controller - A Server Management System") - parser.add_argument('-i', '--ignore', - action='store_true', - help="Ignore session.lock files" - ) + parser.add_argument( + "-i", "--ignore", action="store_true", help="Ignore session.lock files" + ) - parser.add_argument('-v', '--verbose', - action='store_true', - help="Sets logging level to debug." - ) + parser.add_argument( + "-v", "--verbose", action="store_true", help="Sets logging level to debug." + ) - parser.add_argument('-d', '--daemon', - action='store_true', - help="Runs Crafty in daemon mode (no prompt)" - ) + parser.add_argument( + "-d", + "--daemon", + action="store_true", + help="Runs Crafty in daemon mode (no prompt)", + ) args = parser.parse_args() @@ -95,17 +94,18 @@ if __name__ == '__main__': # our session file, helps prevent multiple controller agents on the same machine. helper.create_session_file(ignore=args.ignore) - migration_manager = MigrationManager(database) - migration_manager.up() # Automatically runs migrations + migration_manager.up() # Automatically runs migrations # do our installer stuff fresh_install = installer.is_fresh_install() if fresh_install: console.debug("Fresh install detected") - console.warning("We have detected a fresh install. Please be sure to forward Crafty's port, " + - f"{helper.get_setting('https_port')}, through your router/firewall if you would like to be able to access Crafty remotely.") + console.warning( + "We have detected a fresh install. Please be sure to forward Crafty's port, " + + f"{helper.get_setting('https_port')}, through your router/firewall if you would like to be able to access Crafty remotely." + ) installer.default_settings() else: console.debug("Existing install detected") @@ -116,7 +116,7 @@ if __name__ == '__main__': tasks_manager.start_webserver() # slowing down reporting just for a 1/2 second so messages look cleaner - time.sleep(.5) + time.sleep(0.5) # init servers logger.info("Initializing all servers defined") @@ -137,8 +137,10 @@ if __name__ == '__main__': console.info("Checking Internet. This may take a minute.") if not helper.check_internet(): - console.warning("We have detected the machine running Crafty has no connection to the internet. " + - "Client connections to the server may be limited.") + console.warning( + "We have detected the machine running Crafty has no connection to the internet. " + + "Client connections to the server may be limited." + ) if not controller.check_system_user(): controller.add_system_user() @@ -151,9 +153,13 @@ if __name__ == '__main__': controller.clear_support_status() def sigterm_handler(*sig): - print() # for newline - logger.info(f"Recieved {signal.Signals(sig[0]).name} [{sig[0]}], stopping Crafty...") - console.info(f"Recieved {signal.Signals(sig[0]).name} [{sig[0]}], stopping Crafty...") + print() # for newline + logger.info( + f"Recieved {signal.Signals(sig[0]).name} [{sig[0]}], stopping Crafty..." + ) + console.info( + f"Recieved {signal.Signals(sig[0]).name} [{sig[0]}], stopping Crafty..." + ) tasks_manager._main_graceful_exit() Crafty.universal_exit() @@ -163,7 +169,7 @@ if __name__ == '__main__': try: Crafty.cmdloop() except KeyboardInterrupt: - print() # for newline + print() # for newline logger.info("Recieved SIGINT, stopping Crafty...") console.info("Recieved SIGINT, stopping Crafty...") tasks_manager._main_graceful_exit() From 9b2161d450731c4510618c74f77d2bbfcdebfb58 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 23 Mar 2022 02:51:37 +0000 Subject: [PATCH 11/16] Add pylint compatability for black Marking exemptions and pulling back to new line length (It's gonna hate comments, will fix in a sec) --- .pylintrc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.pylintrc b/.pylintrc index 0dcd80e1..ddb05551 100644 --- a/.pylintrc +++ b/.pylintrc @@ -78,7 +78,9 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". -disable=abstract-method, +disable=C0330, + C0326, + abstract-method, attribute-defined-outside-init, bad-inline-option, bare-except, @@ -306,7 +308,7 @@ indent-after-paren=4 indent-string=' ' # Maximum number of characters on a single line. -max-line-length=150 +max-line-length=88 # Maximum number of lines in a module. max-module-lines=2000 From 2a512d727376ec9004eaf5b166631e166894de07 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 23 Mar 2022 06:06:13 +0000 Subject: [PATCH 12/16] =?UTF-8?q?Fix=20files=20to=20conform=20with=20new?= =?UTF-8?q?=20=E2=9A=ABBlack=20pylintrc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mostly just breaking up strings and comments into new lines Some strings dont require 'f' but keeping in for readability with the rest of the concatinated string --- .../controllers/crafty_perms_controller.py | 12 +- .../controllers/management_controller.py | 20 +- app/classes/controllers/roles_controller.py | 3 +- .../controllers/server_perms_controller.py | 6 +- app/classes/controllers/servers_controller.py | 16 +- app/classes/controllers/users_controller.py | 12 +- app/classes/minecraft/server_props.py | 133 ++++++------- app/classes/minecraft/serverjars.py | 6 +- app/classes/minecraft/stats.py | 47 ++--- app/classes/models/crafty_permissions.py | 19 +- app/classes/models/management.py | 44 ++--- app/classes/models/roles.py | 8 +- app/classes/models/server_permissions.py | 12 +- app/classes/models/servers.py | 29 +-- app/classes/models/users.py | 24 +-- app/classes/shared/authentication.py | 3 +- app/classes/shared/command.py | 3 +- app/classes/shared/file_helpers.py | 6 +- app/classes/shared/helpers.py | 65 ++++--- app/classes/shared/import3.py | 182 +++++++++--------- app/classes/shared/main_controller.py | 82 +++++--- app/classes/shared/main_models.py | 8 +- app/classes/shared/server.py | 126 +++++++----- app/classes/shared/tasks.py | 32 ++- app/classes/web/ajax_handler.py | 16 +- app/classes/web/panel_handler.py | 60 ++++-- app/classes/web/public_handler.py | 5 +- app/classes/web/server_handler.py | 44 +++-- app/classes/web/tornado_handler.py | 15 +- app/classes/web/upload_handler.py | 12 +- app/classes/web/websocket_helper.py | 12 +- 31 files changed, 616 insertions(+), 446 deletions(-) diff --git a/app/classes/controllers/crafty_perms_controller.py b/app/classes/controllers/crafty_perms_controller.py index 4f50d0bb..f47324b2 100644 --- a/app/classes/controllers/crafty_perms_controller.py +++ b/app/classes/controllers/crafty_perms_controller.py @@ -36,15 +36,19 @@ class Crafty_Perms_Controller: @staticmethod def can_add_user(): # Add back argument 'user_id' when you work on this - # TODO: Complete if we need a User Addition limit - # return crafty_permissions.can_add_in_crafty(user_id, Enum_Permissions_Crafty.User_Config) return True + # TODO: Complete if we need a User Addition limit + # return crafty_permissions.can_add_in_crafty( + # user_id, Enum_Permissions_Crafty.User_Config + # ) @staticmethod def can_add_role(): # Add back argument 'user_id' when you work on this - # TODO: Complete if we need a Role Addition limit - # return crafty_permissions.can_add_in_crafty(user_id, Enum_Permissions_Crafty.Roles_Config) return True + # TODO: Complete if we need a Role Addition limit + # return crafty_permissions.can_add_in_crafty( + # user_id, Enum_Permissions_Crafty.Roles_Config + # ) @staticmethod def list_all_crafty_permissions_quantity_limits(): diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py index f2d62d75..f591dbfc 100644 --- a/app/classes/controllers/management_controller.py +++ b/app/classes/controllers/management_controller.py @@ -8,16 +8,16 @@ logger = logging.getLogger(__name__) class Management_Controller: - # ************************************************************************************************ + # ********************************************************************************** # Host_Stats Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_latest_hosts_stats(): return management_helper.get_latest_hosts_stats() - # ************************************************************************************************ + # ********************************************************************************** # Commands Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_unactioned_commands(): return management_helper.get_unactioned_commands() @@ -39,9 +39,9 @@ class Management_Controller: def mark_command_complete(command_id=None): return management_helper.mark_command_complete(command_id) - # ************************************************************************************************ + # ********************************************************************************** # Audit_Log Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_actity_log(): return management_helper.get_actity_log() @@ -58,9 +58,9 @@ class Management_Controller: user_name, user_id, server_id, log_msg, source_ip ) - # ************************************************************************************************ + # ********************************************************************************** # Schedules Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def create_scheduled_task( server_id, @@ -115,9 +115,9 @@ class Management_Controller: def get_schedules_enabled(): return management_helper.get_schedules_enabled() - # ************************************************************************************************ + # ********************************************************************************** # Backups Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_backup_config(server_id): return management_helper.get_backup_config(server_id) diff --git a/app/classes/controllers/roles_controller.py b/app/classes/controllers/roles_controller.py index 224995df..f3bcbad8 100644 --- a/app/classes/controllers/roles_controller.py +++ b/app/classes/controllers/roles_controller.py @@ -45,7 +45,8 @@ class Roles_Controller: server_permissions.get_or_create(role_id, server, permissions_mask) for server in base_data["servers"]: server_permissions.update_role_permission(role_id, server, permissions_mask) - # TODO: This is horribly inefficient and we should be using bulk queries but im going for functionality at this point + # TODO: This is horribly inefficient and we should be using bulk queries + # but im going for functionality at this point server_permissions.delete_roles_permissions(role_id, removed_servers) if up_data: roles_helper.update_role(role_id, up_data) diff --git a/app/classes/controllers/server_perms_controller.py b/app/classes/controllers/server_perms_controller.py index 931f4f1e..9da02d08 100644 --- a/app/classes/controllers/server_perms_controller.py +++ b/app/classes/controllers/server_perms_controller.py @@ -51,11 +51,11 @@ class Server_Perms_Controller: int(role.role_id), int(old_server_id) ), ) - # server_permissions.add_role_server(new_server_id, role.role_id, '00001000') + # server_permissions.add_role_server(new_server_id, role.role_id,"00001000") - # ************************************************************************************************ + # ********************************************************************************** # Servers Permissions Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_permissions_mask(role_id, server_id): return server_permissions.get_permissions_mask(role_id, server_id) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index cfec8375..b7c09935 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -17,9 +17,9 @@ logger = logging.getLogger(__name__) class Servers_Controller: - # ************************************************************************************************ + # ********************************************************************************** # Generic Servers Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def create_server( name: str, @@ -81,9 +81,9 @@ class Servers_Controller: def get_server_data_by_id(server_id): return servers_helper.get_server_data_by_id(server_id) - # ************************************************************************************************ + # ********************************************************************************** # Servers Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_all_defined_servers(): return servers_helper.get_all_defined_servers() @@ -157,9 +157,9 @@ class Servers_Controller: def get_server_friendly_name(server_id): return servers_helper.get_server_friendly_name(server_id) - # ************************************************************************************************ + # ********************************************************************************** # Servers_Stats Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_server_stats_by_id(server_id): return servers_helper.get_server_stats_by_id(server_id) @@ -218,9 +218,9 @@ class Servers_Controller: def get_update_status(server_id): return servers_helper.get_update_status(server_id) - # ************************************************************************************************ + # ********************************************************************************** # Servers Helpers Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_banned_players(server_id): stats = servers_helper.get_server_stats_by_id(server_id) diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py index af590046..933942c6 100644 --- a/app/classes/controllers/users_controller.py +++ b/app/classes/controllers/users_controller.py @@ -14,9 +14,9 @@ logger = logging.getLogger(__name__) class Users_Controller: - # ************************************************************************************************ + # ********************************************************************************** # Users Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_all_users(): return users_helper.get_all_users() @@ -163,9 +163,9 @@ class Users_Controller: _, user = authentication.check(token) return user - # ************************************************************************************************ + # ********************************************************************************** # User Roles Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_user_roles_id(user_id): @@ -187,9 +187,9 @@ class Users_Controller: def user_role_query(user_id): return users_helper.user_role_query(user_id) - # ************************************************************************************************ + # ********************************************************************************** # Api Keys Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_user_api_keys(user_id: str): diff --git a/app/classes/minecraft/server_props.py b/app/classes/minecraft/server_props.py index c70652de..84b8d039 100644 --- a/app/classes/minecraft/server_props.py +++ b/app/classes/minecraft/server_props.py @@ -1,66 +1,67 @@ -import pprint -import os - - -class ServerProps: - def __init__(self, filepath): - self.filepath = filepath - self.props = self._parse() - - def _parse(self): - """Loads and parses the file specified in self.filepath""" - with open(self.filepath, encoding="utf-8") as fp: - line = fp.readline() - d = {} - if os.path.exists(".header"): - os.remove(".header") - while line: - if "#" != line[0]: - s = line - s1 = s[: s.find("=")] - if "\n" in s: - s2 = s[s.find("=") + 1 : s.find("\n")] - else: - s2 = s[s.find("=") + 1 :] - d[s1] = s2 - else: - with open(".header", "a+", encoding="utf-8") as h: - h.write(line) - line = fp.readline() - return d - - def print(self): - """Prints the properties dictionary (using pprint)""" - pprint.pprint(self.props) - - def get(self): - """Returns the properties dictionary""" - return self.props - - def update(self, key, val): - """Updates property in the properties dictionary [ update("pvp", "true") ] and returns boolean condition""" - if key in self.props.keys(): - self.props[key] = val - return True - else: - return False - - def save(self): - """Writes to the new file""" - with open(self.filepath, "a+", encoding="utf-8") as f: - f.truncate(0) - with open(".header", encoding="utf-8") as header: - line = header.readline() - while line: - f.write(line) - line = header.readline() - header.close() - for key, value in self.props.items(): - f.write(key + "=" + value + "\n") - if os.path.exists(".header"): - os.remove(".header") - - @staticmethod - def cleanup(): - if os.path.exists(".header"): - os.remove(".header") +import pprint +import os + + +class ServerProps: + def __init__(self, filepath): + self.filepath = filepath + self.props = self._parse() + + def _parse(self): + # Loads and parses the file specified in self.filepath + with open(self.filepath, encoding="utf-8") as fp: + line = fp.readline() + d = {} + if os.path.exists(".header"): + os.remove(".header") + while line: + if "#" != line[0]: + s = line + s1 = s[: s.find("=")] + if "\n" in s: + s2 = s[s.find("=") + 1 : s.find("\n")] + else: + s2 = s[s.find("=") + 1 :] + d[s1] = s2 + else: + with open(".header", "a+", encoding="utf-8") as h: + h.write(line) + line = fp.readline() + return d + + def print(self): + # Prints the properties dictionary (using pprint) + pprint.pprint(self.props) + + def get(self): + # Returns the properties dictionary + return self.props + + def update(self, key, val): + # Updates property in the properties dictionary [ update("pvp", "true") ] + # and returns boolean condition + if key in self.props.keys(): + self.props[key] = val + return True + else: + return False + + def save(self): + # Writes to the new file + with open(self.filepath, "a+", encoding="utf-8") as f: + f.truncate(0) + with open(".header", encoding="utf-8") as header: + line = header.readline() + while line: + f.write(line) + line = header.readline() + header.close() + for key, value in self.props.items(): + f.write(key + "=" + value + "\n") + if os.path.exists(".header"): + os.remove(".header") + + @staticmethod + def cleanup(): + if os.path.exists(".header"): + os.remove(".header") diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py index 380dbd38..01ad1fb1 100644 --- a/app/classes/minecraft/serverjars.py +++ b/app/classes/minecraft/serverjars.py @@ -137,7 +137,8 @@ class ServerJars: # jar versions for this server versions = self._get_jar_details(s) - # add these versions (a list) to the dict with a key of the server type + # add these versions (a list) to the dict with + # a key of the server type data["servers"].update({s: versions}) # save our cache @@ -177,7 +178,8 @@ class ServerJars: fetch_url = f"{self.base_url}/api/fetchJar/{server}/{version}" server_users = server_permissions.get_server_user_list(server_id) - # We need to make sure the server is registered before we submit a db update for it's stats. + # We need to make sure the server is registered before + # we submit a db update for it's stats. while True: try: Servers_Controller.set_download(server_id) diff --git a/app/classes/minecraft/stats.py b/app/classes/minecraft/stats.py index 3fcc780e..b4db5515 100644 --- a/app/classes/minecraft/stats.py +++ b/app/classes/minecraft/stats.py @@ -67,7 +67,7 @@ class Stats: except Exception as e: logger.error( - f"Unable to get process details for pid: {process_pid} due to error: {e}" + f"Unable to get process details for pid: {process_pid} Error: {e}" ) # Dummy Data @@ -77,7 +77,7 @@ class Stats: } return process_stats - # shamelessly stolen from https://github.com/giampaolo/psutil/blob/master/scripts/disk_usage.py + # Source: https://github.com/giampaolo/psutil/blob/master/scripts/disk_usage.py @staticmethod def _all_disk_usage(): disk_data = [] @@ -207,26 +207,29 @@ class Stats: } ).execute() - # server_stats = stats_to_send.get('servers')# - # - # for server in server_stats: - # Server_Stats.insert({ - # Server_Stats.server_id: server.get('id', 0), - # Server_Stats.started: server.get('started', ""), - # Server_Stats.running: server.get('running', False), - # Server_Stats.cpu: server.get('cpu', 0), - # Server_Stats.mem: server.get('mem', 0), - # Server_Stats.mem_percent: server.get('mem_percent', 0), - # Server_Stats.world_name: server.get('world_name', ""), - # Server_Stats.world_size: server.get('world_size', ""), - # Server_Stats.server_port: server.get('server_port', ""), - # Server_Stats.int_ping_results: server.get('int_ping_results', False), - # Server_Stats.online: server.get("online", False), - # Server_Stats.max: server.get("max", False), - # Server_Stats.players: server.get("players", False), - # Server_Stats.desc: server.get("desc", False), - # Server_Stats.version: server.get("version", False) - # }).execute() + # server_stats = stats_to_send.get("servers") + # for server in server_stats: + # Server_Stats.insert( + # { + # Server_Stats.server_id: server.get("id", 0), + # Server_Stats.started: server.get("started", ""), + # Server_Stats.running: server.get("running", False), + # Server_Stats.cpu: server.get("cpu", 0), + # Server_Stats.mem: server.get("mem", 0), + # Server_Stats.mem_percent: server.get("mem_percent", 0), + # Server_Stats.world_name: server.get("world_name", ""), + # Server_Stats.world_size: server.get("world_size", ""), + # Server_Stats.server_port: server.get("server_port", ""), + # Server_Stats.int_ping_results: server.get( + # "int_ping_results", False + # ), + # Server_Stats.online: server.get("online", False), + # Server_Stats.max: server.get("max", False), + # Server_Stats.players: server.get("players", False), + # Server_Stats.desc: server.get("desc", False), + # Server_Stats.version: server.get("version", False), + # } + # ).execute() # delete old data max_age = helper.get_setting("history_max_age") diff --git a/app/classes/models/crafty_permissions.py b/app/classes/models/crafty_permissions.py index 46b02315..fbe8ab0e 100644 --- a/app/classes/models/crafty_permissions.py +++ b/app/classes/models/crafty_permissions.py @@ -26,9 +26,9 @@ database = SqliteDatabase( ) -# ************************************************************************************************ +# ********************************************************************************** # User_Crafty Class -# ************************************************************************************************ +# ********************************************************************************** class User_Crafty(Model): user_id = ForeignKeyField(Users, backref="users_crafty") permissions = CharField(default="00000000") @@ -44,9 +44,9 @@ class User_Crafty(Model): database = database -# ************************************************************************************************ +# ********************************************************************************** # Crafty Permissions Class -# ************************************************************************************************ +# ********************************************************************************** class Enum_Permissions_Crafty(Enum): Server_Creation = 0 User_Config = 1 @@ -54,10 +54,9 @@ class Enum_Permissions_Crafty(Enum): class Permissions_Crafty: - - # ************************************************************************************************ + # ********************************************************************************** # Crafty Permissions Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_permissions_list(): permissions_list = [] @@ -113,15 +112,15 @@ class Permissions_Crafty: def get_permission_quantity_list(user_id): user_crafty = crafty_permissions.get_User_Crafty(user_id) quantity_list = { - Enum_Permissions_Crafty.Server_Creation.name: user_crafty.limit_server_creation, + Enum_Permissions_Crafty.Server_Creation.name: user_crafty.limit_server_creation, # pylint: disable=line-too-long Enum_Permissions_Crafty.User_Config.name: user_crafty.limit_user_creation, Enum_Permissions_Crafty.Roles_Config.name: user_crafty.limit_role_creation, } return quantity_list - # ************************************************************************************************ + # ********************************************************************************** # User_Crafty Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_User_Crafty(user_id): try: diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 2e55bd49..039ceeeb 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -33,9 +33,9 @@ database = SqliteDatabase( helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} ) -# ************************************************************************************************ +# ********************************************************************************** # Audit_Log Class -# ************************************************************************************************ +# ********************************************************************************** class Audit_Log(Model): audit_id = AutoField() created = DateTimeField(default=datetime.datetime.now) @@ -51,9 +51,9 @@ class Audit_Log(Model): database = database -# ************************************************************************************************ +# ********************************************************************************** # Host_Stats Class -# ************************************************************************************************ +# ********************************************************************************** class Host_Stats(Model): time = DateTimeField(default=datetime.datetime.now, index=True) boot_time = CharField(default="") @@ -71,9 +71,9 @@ class Host_Stats(Model): database = database -# ************************************************************************************************ +# ********************************************************************************** # Commands Class -# ************************************************************************************************ +# ********************************************************************************** class Commands(Model): command_id = AutoField() created = DateTimeField(default=datetime.datetime.now) @@ -88,9 +88,9 @@ class Commands(Model): database = database -# ************************************************************************************************ +# ********************************************************************************** # Webhooks Class -# ************************************************************************************************ +# ********************************************************************************** class Webhooks(Model): id = AutoField() name = CharField(max_length=64, unique=True, index=True) @@ -104,9 +104,9 @@ class Webhooks(Model): database = database -# ************************************************************************************************ +# ********************************************************************************** # Schedules Class -# ************************************************************************************************ +# ********************************************************************************** class Schedules(Model): schedule_id = IntegerField(unique=True, primary_key=True) server_id = ForeignKeyField(Servers, backref="schedule_server") @@ -127,9 +127,9 @@ class Schedules(Model): database = database -# ************************************************************************************************ +# ********************************************************************************** # Backups Class -# ************************************************************************************************ +# ********************************************************************************** class Backups(Model): excluded_dirs = CharField(null=True) max_backups = IntegerField() @@ -143,18 +143,18 @@ class Backups(Model): class helpers_management: - # ************************************************************************************************ + # ********************************************************************************** # Host_Stats Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_latest_hosts_stats(): # pylint: disable=no-member query = Host_Stats.select().order_by(Host_Stats.id.desc()).get() return model_to_dict(query) - # ************************************************************************************************ + # ********************************************************************************** # Commands Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def add_command(server_id, user_id, remote_ip, command): Commands.insert( @@ -179,9 +179,9 @@ class helpers_management: Commands.command_id == command_id ).execute() - # ************************************************************************************************ + # ********************************************************************************** # Audit_Log Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_actity_log(): q = Audit_Log.select() @@ -243,9 +243,9 @@ class helpers_management: else: return - # ************************************************************************************************ + # ********************************************************************************** # Schedules Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def create_scheduled_task( server_id, @@ -325,9 +325,9 @@ class helpers_management: # pylint: disable=singleton-comparison return Schedules.select().where(Schedules.enabled == True).execute() - # ************************************************************************************************ + # ********************************************************************************** # Backups Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_backup_config(server_id): try: diff --git a/app/classes/models/roles.py b/app/classes/models/roles.py index 6724aafb..ff705e96 100644 --- a/app/classes/models/roles.py +++ b/app/classes/models/roles.py @@ -24,9 +24,9 @@ database = SqliteDatabase( helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} ) -# ************************************************************************************************ +# ********************************************************************************** # Roles Class -# ************************************************************************************************ +# ********************************************************************************** class Roles(Model): role_id = AutoField() created = DateTimeField(default=datetime.datetime.now) @@ -38,9 +38,9 @@ class Roles(Model): database = database -# ************************************************************************************************ +# ********************************************************************************** # Roles Helpers -# ************************************************************************************************ +# ********************************************************************************** class helper_roles: @staticmethod def get_all_roles(): diff --git a/app/classes/models/server_permissions.py b/app/classes/models/server_permissions.py index a4024873..92ecbd09 100644 --- a/app/classes/models/server_permissions.py +++ b/app/classes/models/server_permissions.py @@ -27,9 +27,9 @@ database = SqliteDatabase( helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} ) -# ************************************************************************************************ +# ********************************************************************************** # Role Servers Class -# ************************************************************************************************ +# ********************************************************************************** class Role_Servers(Model): role_id = ForeignKeyField(Roles, backref="role_server") server_id = ForeignKeyField(Servers, backref="role_server") @@ -41,9 +41,9 @@ class Role_Servers(Model): database = database -# ************************************************************************************************ +# ********************************************************************************** # Servers Permissions Class -# ************************************************************************************************ +# ********************************************************************************** class Enum_Permissions_Server(Enum): Commands = 0 Terminal = 1 @@ -104,9 +104,9 @@ class Permissions_Servers: permissions_list.append(member[1]) return permissions_list - # ************************************************************************************************ + # ********************************************************************************** # Role_Servers Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_role_servers_from_role_id(roleid): return Role_Servers.select().where(Role_Servers.role_id == roleid) diff --git a/app/classes/models/servers.py b/app/classes/models/servers.py index 8d5526ec..f9c777a1 100644 --- a/app/classes/models/servers.py +++ b/app/classes/models/servers.py @@ -27,9 +27,9 @@ database = SqliteDatabase( helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} ) -# ************************************************************************************************ +# ********************************************************************************** # Servers Class -# ************************************************************************************************ +# ********************************************************************************** class Servers(Model): server_id = AutoField() created = DateTimeField(default=datetime.datetime.now) @@ -55,9 +55,9 @@ class Servers(Model): database = database -# ************************************************************************************************ +# ********************************************************************************** # Servers Stats Class -# ************************************************************************************************ +# ********************************************************************************** class Server_Stats(Model): stats_id = AutoField() created = DateTimeField(default=datetime.datetime.now) @@ -87,14 +87,14 @@ class Server_Stats(Model): database = database -# ************************************************************************************************ +# ********************************************************************************** # Servers Class -# ************************************************************************************************ +# ********************************************************************************** class helper_servers: - # ************************************************************************************************ + # ********************************************************************************** # Generic Servers Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def create_server( name: str, @@ -152,9 +152,9 @@ class helper_servers: except IndexError: return {} - # ************************************************************************************************ + # ********************************************************************************** # Servers Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_all_defined_servers(): query = Servers.select() @@ -188,12 +188,15 @@ class helper_servers: @staticmethod def get_server_friendly_name(server_id): server_data = servers_helper.get_server_data_by_id(server_id) - friendly_name = f"{server_data.get('server_name', None)} with ID: {server_data.get('server_id', 0)}" + friendly_name = ( + f"{server_data.get('server_name', None)} " + f"with ID: {server_data.get('server_id', 0)}" + ) return friendly_name - # ************************************************************************************************ + # ********************************************************************************** # Servers_Stats Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_latest_server_stats(server_id): return ( diff --git a/app/classes/models/users.py b/app/classes/models/users.py index 24020e61..a78862ba 100644 --- a/app/classes/models/users.py +++ b/app/classes/models/users.py @@ -30,9 +30,9 @@ database = SqliteDatabase( helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} ) -# ************************************************************************************************ +# ********************************************************************************** # Users Class -# ************************************************************************************************ +# ********************************************************************************** class Users(Model): user_id = AutoField() created = DateTimeField(default=datetime.datetime.now) @@ -55,9 +55,9 @@ class Users(Model): database = database -# ************************************************************************************************ +# ********************************************************************************** # API Keys Class -# ************************************************************************************************ +# ********************************************************************************** class ApiKeys(Model): token_id = AutoField() name = CharField(default="", unique=True, index=True) @@ -72,9 +72,9 @@ class ApiKeys(Model): database = database -# ************************************************************************************************ +# ********************************************************************************** # User Roles Class -# ************************************************************************************************ +# ********************************************************************************** class User_Roles(Model): user_id = ForeignKeyField(Users, backref="user_role") role_id = ForeignKeyField(Roles, backref="user_role") @@ -85,9 +85,9 @@ class User_Roles(Model): database = database -# ************************************************************************************************ +# ********************************************************************************** # Users Helpers -# ************************************************************************************************ +# ********************************************************************************** class helper_users: @staticmethod def get_by_id(user_id): @@ -260,9 +260,9 @@ class helper_users: return False return True - # ************************************************************************************************ + # ********************************************************************************** # User_Roles Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_or_create(user_id, role_id): @@ -335,9 +335,9 @@ class helper_users: def remove_roles_from_role_id(role_id): User_Roles.delete().where(User_Roles.role_id == role_id).execute() - # ************************************************************************************************ + # ********************************************************************************** # ApiKeys Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def get_user_api_keys(user_id: str): diff --git a/app/classes/shared/authentication.py b/app/classes/shared/authentication.py index 6592fdd3..6f4d5bcc 100644 --- a/app/classes/shared/authentication.py +++ b/app/classes/shared/authentication.py @@ -63,7 +63,8 @@ class Authentication: return None user_id: str = data["user_id"] user = users_helper.get_user(user_id) - # TODO: Have a cache or something so we don't constantly have to query the database + # TODO: Have a cache or something so we don't constantly + # have to query the database if int(user.get("valid_tokens_from").timestamp()) < iat: # Success! return key, data, user diff --git a/app/classes/shared/command.py b/app/classes/shared/command.py index 8907f4c5..0df70648 100644 --- a/app/classes/shared/command.py +++ b/app/classes/shared/command.py @@ -56,7 +56,8 @@ class MainPrompt(cmd.Cmd): for thread in threading.enumerate(): if sys.version_info >= (3, 8): print( - f"Name: {thread.name} Identifier: {thread.ident} TID/PID: {thread.native_id}" + f"Name: {thread.name} Identifier: " + f"{thread.ident} TID/PID: {thread.native_id}" ) else: print(f"Name: {thread.name} Identifier: {thread.ident}") diff --git a/app/classes/shared/file_helpers.py b/app/classes/shared/file_helpers.py index a706d115..bc21da51 100644 --- a/app/classes/shared/file_helpers.py +++ b/app/classes/shared/file_helpers.py @@ -76,7 +76,8 @@ class FileHelpers: except Exception as e: logger.warning( - f"Error backing up: {os.path.join(root, file)}! - Error was: {e}" + f"Error backing up: {os.path.join(root, file)}!" + f" - Error was: {e}" ) return True @@ -104,7 +105,8 @@ class FileHelpers: except Exception as e: logger.warning( - f"Error backing up: {os.path.join(root, file)}! - Error was: {e}" + f"Error backing up: {os.path.join(root, file)}!" + f" - Error was: {e}" ) return True diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index cbc75b2a..6deb85ee 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -142,28 +142,26 @@ class Helpers: ci = -1 # command index - pointer to the argument we're building in cmd_out np = True # whether we're creating a new argument/parameter esc = False # whether an escape character was encountered - stch = None # if we're dealing with a quote, save the quote type here. Nested quotes to be dealt with by the command + stch = None # if we're dealing with a quote, save the quote type here. + # Nested quotes to be dealt with by the command for c in cmd_in: # for character in string - if ( - np - ): # if set, begin a new argument and increment the command index. Continue the loop. + if np: # if set, begin a new argument and increment the command index. + # Continue the loop. if c == " ": continue else: ci += 1 cmd_out.append("") np = False - if ( - esc - ): # if we encountered an escape character on the last loop, append this char regardless of what it is + if esc: # if we encountered an escape character on the last loop, + # append this char regardless of what it is if c not in Helpers.allowed_quotes: cmd_out[ci] += "\\" cmd_out[ci] += c esc = False else: - if ( - c == "\\" - ): # if the current character is an escape character, set the esc flag and continue to next loop + if c == "\\": # if the current character is an escape character, + # set the esc flag and continue to next loop esc = True elif ( c == " " and stch is None @@ -172,12 +170,13 @@ class Helpers: np = True elif ( c == stch - ): # if we encounter the character that matches our start quote, end the quote and continue to next loop + ): # if we encounter the character that matches our start quote, + # end the quote and continue to next loop stch = None elif stch is None and ( c in Helpers.allowed_quotes - ): # if we're not in the middle of a quote and we get a quotable character, - # start a quote and proceed to the next loop + ): # if we're not in the middle of a quote and we get a quotable + # character, start a quote and proceed to the next loop stch = c else: # else, just store the character in the current arg cmd_out[ci] += c @@ -236,8 +235,9 @@ class Helpers: def get_announcements(): r = requests.get("https://craftycontrol.com/notify.json", timeout=2) data = ( - '[{"id":"1","date":"Unknown","title":"Error getting Announcements","desc":"Error getting ' - 'Announcements","link":""}] ' + '[{"id":"1","date":"Unknown",' + '"title":"Error getting Announcements",' + '"desc":"Error getting Announcements","link":""}]' ) if r.status_code in [200, 201]: @@ -337,7 +337,8 @@ class Helpers: # get file size fsize = f.tell() - # set pos @ last n chars (buffer from above = number of lines * avg_line_length) + # set pos @ last n chars + # (buffer from above = number of lines * avg_line_length) f.seek(max(fsize - line_buffer, 0), 0) # read file til the end @@ -455,7 +456,8 @@ class Helpers: @staticmethod def calc_percent(source_path, dest_path): - # calculates percentable of zip from drive. Not with compression. For backups and support logs + # calculates percentable of zip from drive. Not with compression. + # (For backups and support logs) source_size = 0 files_count = 0 for path, _dirs, files in os.walk(source_path): @@ -539,13 +541,15 @@ class Helpers: started = data.get("started") if psutil.pid_exists(pid): console.critical( - f"Another Crafty Controller agent seems to be running...\npid: {pid} \nstarted on: {started}" + f"Another Crafty Controller agent seems to be running..." + f"\npid: {pid} \nstarted on: {started}" ) logger.critical("Found running crafty process. Exiting.") sys.exit(1) else: logger.info( - "No process found for pid. Assuming crafty crashed. Deleting stale session.lock" + "No process found for pid. Assuming " + "crafty crashed. Deleting stale session.lock" ) os.remove(self.session_file) @@ -564,7 +568,8 @@ class Helpers: with open(self.session_file, "w", encoding="utf-8") as f: json.dump(session_data, f, indent=True) - # because this is a recursive function, we will return bytes, and set human readable later + # because this is a recursive function, we will return bytes, + # and set human readable later def get_dir_size(self, path: str): total = 0 for entry in os.scandir(path): @@ -610,7 +615,8 @@ class Helpers: """ ensures a directory exists - Checks for the existence of a directory, if the directory isn't there, this function creates the directory + Checks for the existence of a directory, if the directory isn't there, + this function creates the directory Args: path (string): the path you are checking for @@ -769,7 +775,8 @@ class Helpers: class="tree-nested d-block tree-ctx-item tree-file tree-item" data-path="{dpath}" data-name="{filename}" - onclick="clickOnFile(event)">{filename}""" + onclick="clickOnFile(event)"> + {filename}""" return output @staticmethod @@ -805,7 +812,8 @@ class Helpers: class="tree-nested d-block tree-ctx-item tree-file tree-item" data-path="{dpath}" data-name="{filename}" - onclick="clickOnFile(event)">{filename}""" + onclick="clickOnFile(event)"> + {filename}""" output += "
                  \n" return output @@ -883,13 +891,16 @@ class Helpers: @staticmethod def in_path(parent_path, child_path): - # Smooth out relative path names, note: if you are concerned about symbolic links, you should use os.path.realpath too + # Smooth out relative path names, note: if you are concerned about + # symbolic links, you should use os.path.realpath too parent_path = os.path.abspath(parent_path) child_path = os.path.abspath(child_path) - # Compare the common path of the parent and child path with the common path of just the parent path. - # Using the commonpath method on just the parent path will regularise the path name in the same way - # as the comparison that deals with both paths, removing any trailing path separator + # Compare the common path of the parent and child path with the + # common path of just the parent path. Using the commonpath method + # on just the parent path will regularise the path name in the same way + # as the comparison that deals with both paths, removing any trailing + # path separator return os.path.commonpath([parent_path]) == os.path.commonpath( [parent_path, child_path] ) diff --git a/app/classes/shared/import3.py b/app/classes/shared/import3.py index 024dd444..404639d4 100644 --- a/app/classes/shared/import3.py +++ b/app/classes/shared/import3.py @@ -1,87 +1,95 @@ -import json -import os -import logging - -from app.classes.controllers.users_controller import users_helper -from app.classes.shared.main_controller import Controller -from app.classes.shared.console import console - -logger = logging.getLogger(__name__) - - -class import3: - def __init__(self): - self.controller = Controller() - - def start_import(self): - folder = os.path.normpath( - input( - "Please input the path to the migrations folder in your installation of Crafty 3: " - ) - ) - if not os.path.exists(folder): - console.info( - "Crafty cannot find the path you entered. Does Crafty's user have permission to access it?" - ) - console.info("Please run the import3 command again and enter a valid path.") - else: - with open(os.path.join(folder, "users.json"), encoding="utf-8") as f: - user_json = json.loads(f.read()) - with open(os.path.join(folder, "mc_settings.json"), encoding="utf-8") as f: - servers_json = json.loads(f.read()) - self.import_users(user_json) - self.import_servers(servers_json, self.controller) - - @staticmethod - def import_users(json_data): - # If there is only one user to import json needs to call the data differently - if isinstance(json_data, list): - for user in json_data: - users_helper.add_rawpass_user(user["username"], user["password"]) - console.info(f"Imported user {user['username']} from Crafty 3") - logger.info(f"Imported user {user['username']} from Crafty 3") - else: - console.info( - "There is only one user detected. Cannot create duplicate Admin account." - ) - logger.info( - "There is only one user detected. Cannot create duplicate Admin account." - ) - - @staticmethod - def import_servers(json_data, controller): - # If there is only one server to import json needs to call the data differently - if isinstance(json_data, list): - for server in json_data: - new_server_id = controller.import_jar_server( - server_name=server["server_name"], - server_path=server["server_path"], - server_jar=server["server_jar"], - min_mem=(int(server["memory_min"]) / 1000), - max_mem=(int(server["memory_max"]) / 1000), - port=server["server_port"], - ) - console.info( - f"Imported server {server['server_name']}[{server['id']}] from Crafty 3 to new server id {new_server_id}" - ) - logger.info( - f"Imported server {server['server_name']}[{server['id']}] from Crafty 3 to new server id {new_server_id}" - ) - else: - new_server_id = controller.import_jar_server( - server_name=json_data["server_name"], - server_path=json_data["server_path"], - server_jar=json_data["server_jar"], - min_mem=(int(json_data["memory_min"]) / 1000), - max_mem=(int(json_data["memory_max"]) / 1000), - port=json_data["server_port"], - ) - console.info( - f"Imported server {json_data['server_name']}[{json_data['id']}] from Crafty 3 to new server id {new_server_id}" - ) - logger.info( - f"Imported server {json_data['server_name']}[{json_data['id']}] from Crafty 3 to new server id {new_server_id}" - ) - - -import3 = import3() +import json +import os +import logging + +from app.classes.controllers.users_controller import users_helper +from app.classes.shared.main_controller import Controller +from app.classes.shared.console import console + +logger = logging.getLogger(__name__) + + +class import3: + def __init__(self): + self.controller = Controller() + + def start_import(self): + folder = os.path.normpath( + input( + "Please input the path to the migrations folder " + "in your installation of Crafty 3: " + ) + ) + if not os.path.exists(folder): + console.info( + "Crafty cannot find the path you entered. " + "Does Crafty's user have permission to access it?" + ) + console.info("Please run the import3 command again and enter a valid path.") + else: + with open(os.path.join(folder, "users.json"), encoding="utf-8") as f: + user_json = json.loads(f.read()) + with open(os.path.join(folder, "mc_settings.json"), encoding="utf-8") as f: + servers_json = json.loads(f.read()) + self.import_users(user_json) + self.import_servers(servers_json, self.controller) + + @staticmethod + def import_users(json_data): + # If there is only one user to import json needs to call the data differently + if isinstance(json_data, list): + for user in json_data: + users_helper.add_rawpass_user(user["username"], user["password"]) + console.info(f"Imported user {user['username']} from Crafty 3") + logger.info(f"Imported user {user['username']} from Crafty 3") + else: + console.info( + "There is only one user detected. " + "Cannot create duplicate Admin account." + ) + logger.info( + "There is only one user detected. " + "Cannot create duplicate Admin account." + ) + + @staticmethod + def import_servers(json_data, controller): + # If there is only one server to import json needs to call the data differently + if isinstance(json_data, list): + for server in json_data: + new_server_id = controller.import_jar_server( + server_name=server["server_name"], + server_path=server["server_path"], + server_jar=server["server_jar"], + min_mem=(int(server["memory_min"]) / 1000), + max_mem=(int(server["memory_max"]) / 1000), + port=server["server_port"], + ) + console.info( + f"Imported server {server['server_name']}[{server['id']}] " + f"from Crafty 3 to new server id {new_server_id}" + ) + logger.info( + f"Imported server {server['server_name']}[{server['id']}] " + f"from Crafty 3 to new server id {new_server_id}" + ) + else: + new_server_id = controller.import_jar_server( + server_name=json_data["server_name"], + server_path=json_data["server_path"], + server_jar=json_data["server_jar"], + min_mem=(int(json_data["memory_min"]) / 1000), + max_mem=(int(json_data["memory_max"]) / 1000), + port=json_data["server_port"], + ) + console.info( + f"Imported server {json_data['server_name']}[{json_data['id']}] " + f"from Crafty 3 to new server id {new_server_id}" + ) + logger.info( + f"Imported server {json_data['server_name']}[{json_data['id']}] " + f"from Crafty 3 to new server id {new_server_id}" + ) + + +import3 = import3() diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index eefefb7e..71a5f741 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -64,7 +64,8 @@ class Controller: if known_server == server_id_to_check: logger.info( - f"skipping initialization of server {server_id_to_check} because it is already loaded" + f"skipping initialization of server {server_id_to_check} " + f"because it is already loaded" ) return True @@ -86,11 +87,13 @@ class Controller: helper.get_os_understandable_path(s["path"]) ): logger.warning( - f"Unable to find server {s['server_name']} at path {s['path']}. Skipping this server" + f"Unable to find server {s['server_name']} at path {s['path']}. " + f"Skipping this server" ) console.warning( - f"Unable to find server {s['server_name']} at path {s['path']}. Skipping this server" + f"Unable to find server {s['server_name']} at path {s['path']}. " + f"Skipping this server" ) continue @@ -180,7 +183,8 @@ class Controller: auth_servers.append(server) else: logger.info( - f"Logs permission not available for server {server['server_name']}. Skipping." + f"Logs permission not available for server " + f"{server['server_name']}. Skipping." ) # we'll iterate through our list of log paths from auth servers. for server in auth_servers: @@ -242,7 +246,8 @@ class Controller: def crash_detection(self, server_obj): svr = self.get_server_obj(server_obj.server_id) # start or stop crash detection depending upon user preference - # The below functions check to see if the server is running. They only execute if it's running. + # The below functions check to see if the server is running. + # They only execute if it's running. if server_obj.crash_detection == 1: svr.start_crash_detection() else: @@ -366,11 +371,18 @@ class Controller: logger.error(f"Unable to create required server files due to :{e}") return False - # must remain non-fstring due to string addtion if helper.is_os_windows(): - server_command = f'java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar "{full_jar_path}" nogui' + server_command = ( + f"java -Xms{helper.float_to_string(min_mem)}M " + f"-Xmx{helper.float_to_string(max_mem)}M " + f'-jar "{full_jar_path}" nogui' + ) else: - server_command = f"java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar {full_jar_path} nogui" + server_command = ( + f"java -Xms{helper.float_to_string(min_mem)}M " + f"-Xmx{helper.float_to_string(max_mem)}M " + f"-jar {full_jar_path} nogui" + ) server_log_file = f"{server_dir}/logs/latest.log" server_stop = "stop" @@ -441,7 +453,8 @@ class Controller: has_properties = True if not has_properties: logger.info( - f"No server.properties found on zip file import. Creating one with port selection of {str(port)}" + f"No server.properties found on zip file import. " + f"Creating one with port selection of {str(port)}" ) with open( os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" @@ -451,11 +464,18 @@ class Controller: full_jar_path = os.path.join(new_server_dir, server_jar) - # due to adding strings this must not be an fstring if helper.is_os_windows(): - server_command = f'java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar "{full_jar_path}" nogui' + server_command = ( + f"java -Xms{helper.float_to_string(min_mem)}M " + f"-Xmx{helper.float_to_string(max_mem)}M " + f'-jar "{full_jar_path}" nogui' + ) else: - server_command = f"java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar {full_jar_path} nogui" + server_command = ( + f"java -Xms{helper.float_to_string(min_mem)}M " + f"-Xmx{helper.float_to_string(max_mem)}M " + f"-jar {full_jar_path} nogui" + ) server_log_file = f"{new_server_dir}/logs/latest.log" server_stop = "stop" @@ -512,7 +532,8 @@ class Controller: logger.error(f"ERROR IN ZIP IMPORT: {ex}") if not has_properties: logger.info( - f"No server.properties found on zip file import. Creating one with port selection of {str(port)}" + f"No server.properties found on zip file import. " + f"Creating one with port selection of {str(port)}" ) with open( os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" @@ -522,11 +543,18 @@ class Controller: full_jar_path = os.path.join(new_server_dir, server_jar) - # due to strings being added we need to leave this as not an fstring if helper.is_os_windows(): - server_command = f'java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar "{full_jar_path}" nogui' + server_command = ( + f"java -Xms{helper.float_to_string(min_mem)}M " + f"-Xmx{helper.float_to_string(max_mem)}M " + f'-jar "{full_jar_path}" nogui' + ) else: - server_command = f"java -Xms{helper.float_to_string(min_mem)}M -Xmx{helper.float_to_string(max_mem)}M -jar {full_jar_path} nogui" + server_command = ( + f"java -Xms{helper.float_to_string(min_mem)}M " + f"-Xmx{helper.float_to_string(max_mem)}M " + f"-jar {full_jar_path} nogui" + ) logger.debug("command: " + server_command) server_log_file = f"{new_server_dir}/logs/latest.log" server_stop = "stop" @@ -545,9 +573,9 @@ class Controller: ) return new_id - # ************************************************************************************************ + # ********************************************************************************** # BEDROCK IMPORTS - # ************************************************************************************************ + # ********************************************************************************** def import_bedrock_server( self, server_name: str, server_path: str, server_exe: str, port: int @@ -575,7 +603,8 @@ class Controller: has_properties = True if not has_properties: logger.info( - f"No server.properties found on zip file import. Creating one with port selection of {str(port)}" + f"No server.properties found on zip file import. " + f"Creating one with port selection of {str(port)}" ) with open( os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" @@ -585,7 +614,6 @@ class Controller: full_jar_path = os.path.join(new_server_dir, server_exe) - # due to adding strings this must not be an fstring if helper.is_os_windows(): server_command = f'"{full_jar_path}"' else: @@ -644,7 +672,8 @@ class Controller: logger.error(f"ERROR IN ZIP IMPORT: {ex}") if not has_properties: logger.info( - f"No server.properties found on zip file import. Creating one with port selection of {str(port)}" + f"No server.properties found on zip file import. " + f"Creating one with port selection of {str(port)}" ) with open( os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" @@ -654,7 +683,6 @@ class Controller: full_jar_path = os.path.join(new_server_dir, server_exe) - # due to strings being added we need to leave this as not an fstring if helper.is_os_windows(): server_command = f'"{full_jar_path}"' else: @@ -681,9 +709,9 @@ class Controller: return new_id - # ************************************************************************************************ + # ********************************************************************************** # BEDROCK IMPORTS END - # ************************************************************************************************ + # ********************************************************************************** def rename_backup_dir(self, old_server_id, new_server_id, new_uuid): server_data = self.servers.get_server_data_by_id(old_server_id) @@ -743,7 +771,8 @@ class Controller: encoding="utf-8", ) as f: f.write( - "The server is managed by Crafty Controller.\n Leave this directory/files alone please" + "The server is managed by Crafty Controller.\n " + "Leave this directory/files alone please" ) f.close() @@ -782,7 +811,8 @@ class Controller: ) except Exception as e: logger.error( - f"Unable to delete server files for server with ID: {server_id} with error logged: {e}" + f"Unable to delete server files for server with ID: " + f"{server_id} with error logged: {e}" ) if helper.check_path_exists( self.servers.get_server_data_by_id(server_id)["backup_path"] diff --git a/app/classes/shared/main_models.py b/app/classes/shared/main_models.py index b1a79bb9..dcd4296f 100644 --- a/app/classes/shared/main_models.py +++ b/app/classes/shared/main_models.py @@ -53,9 +53,9 @@ class db_builder: class db_shortcuts: - # ************************************************************************************************ + # ********************************************************************************** # Generic Databse Methods - # ************************************************************************************************ + # ********************************************************************************** @staticmethod def return_rows(query): rows = [] @@ -75,8 +75,8 @@ class db_shortcuts: return data -# ************************************************************************************************ +# ********************************************************************************** # Static Accessors -# ************************************************************************************************ +# ********************************************************************************** installer = db_builder() db_helper = db_shortcuts() diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 5c7541e2..7f846b6e 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -67,7 +67,8 @@ class ServerOutBuf: while True: if self.proc.poll() is None: char = self.proc.stdout.read(1).decode("utf-8", "ignore") - # TODO: we may want to benchmark reading in blocks and userspace processing it later, reads are kind of expensive as a syscall + # TODO: we may want to benchmark reading in blocks and userspace + # processing it later, reads are kind of expensive as a syscall self.process_byte(char) else: flush = self.proc.stdout.read().decode("utf-8", "ignore") @@ -82,7 +83,8 @@ class ServerOutBuf: logger.debug("Broadcasting new virtual terminal line") - # TODO: Do not send data to clients who do not have permission to view this server's console + # TODO: Do not send data to clients who do not have permission to view + # this server's console websocket_helper.broadcast_page_params( "/panel/server_detail", {"id": self.server_id}, @@ -91,9 +93,9 @@ class ServerOutBuf: ) -# ************************************************************************************************ +# ********************************************************************************** # Minecraft Server Class -# ************************************************************************************************ +# ********************************************************************************** class Server: def __init__(self, stats): # holders for our process @@ -122,9 +124,9 @@ class Server: servers_helper.server_crash_reset(self.server_id) servers_helper.set_update(self.server_id, False) - # ************************************************************************************************ + # ********************************************************************************** # Minecraft Server Management - # ************************************************************************************************ + # ********************************************************************************** def reload_server_settings(self): server_data = servers_helper.get_server_data_by_id(self.server_id) self.settings = server_data @@ -135,7 +137,9 @@ class Server: autoStart = server_data_obj["auto_start"] logger.info( - f"Creating Server object: {serverId} | Server Name: {serverName} | Auto Start: {autoStart}" + f"Creating Server object: {serverId} | " + f"Server Name: {serverName} | " + f"Auto Start: {autoStart}" ) self.server_id = serverId self.name = serverName @@ -259,7 +263,7 @@ class Server: console.info(f"Launching Server {self.name} with command {self.server_command}") # Checks for eula. Creates one if none detected. - # If EULA is detected and not set to one of these true vaiants we offer to set it true. + # If EULA is detected and not set to true we offer to set it true. if helper.check_file_exists(os.path.join(self.settings["path"], "eula.txt")): f = open( os.path.join(self.settings["path"], "eula.txt"), "r", encoding="utf-8" @@ -289,7 +293,8 @@ class Server: ) else: logger.error( - "Autostart failed due to EULA being false. Agree not sent due to auto start." + "Autostart failed due to EULA being false. " + "Agree not sent due to auto start." ) return False return False @@ -334,7 +339,8 @@ class Server: == "minecraft-bedrock" ): logger.info( - f"Bedrock and Unix detected for server {self.name}. Switching to appropriate execution string" + f"Bedrock and Unix detected for server {self.name}. " + f"Switching to appropriate execution string" ) my_env = os.environ my_env["LD_LIBRARY_PATH"] = self.server_path @@ -456,18 +462,22 @@ class Server: websocket_helper.broadcast_user(user, "send_start_reload", {}) else: logger.warning( - f"Server PID {self.process.pid} died right after starting - is this a server config issue?" + f"Server PID {self.process.pid} died right after starting " + f"- is this a server config issue?" ) console.warning( - f"Server PID {self.process.pid} died right after starting - is this a server config issue?" + f"Server PID {self.process.pid} died right after starting " + f"- is this a server config issue?" ) if self.settings["crash_detection"]: logger.info( - f"Server {self.name} has crash detection enabled - starting watcher task" + f"Server {self.name} has crash detection enabled " + f"- starting watcher task" ) console.info( - f"Server {self.name} has crash detection enabled - starting watcher task" + f"Server {self.name} has crash detection enabled " + f"- starting watcher task" ) self.server_scheduler.add_job( @@ -484,24 +494,29 @@ class Server: ) def stop_crash_detection(self): - # This is only used if the crash detection settings change while the server is running. + # This is only used if the crash detection settings change + # while the server is running. if self.check_running(): logger.info(f"Detected crash detection shut off for server {self.name}") try: self.server_scheduler.remove_job("c_" + str(self.server_id)) except: logger.error( - f"Removing crash watcher for server {self.name} failed. Assuming it was never started." + f"Removing crash watcher for server {self.name} failed. " + f"Assuming it was never started." ) def start_crash_detection(self): - # This is only used if the crash detection settings change while the server is running. + # This is only used if the crash detection settings change + # while the server is running. if self.check_running(): logger.info( - f"Server {self.name} has crash detection enabled - starting watcher task" + f"Server {self.name} has crash detection enabled " + f"- starting watcher task" ) console.info( - f"Server {self.name} has crash detection enabled - starting watcher task" + f"Server {self.name} has crash detection enabled " + "- starting watcher task" ) self.server_scheduler.add_job( self.detect_crash, "interval", seconds=30, id=f"c_{self.server_id}" @@ -523,7 +538,8 @@ class Server: self.server_scheduler.remove_job("c_" + str(self.server_id)) except: logger.error( - f"Removing crash watcher for server {self.name} failed. Assuming it was never started." + f"Removing crash watcher for server {self.name} failed. " + f"Assuming it was never started." ) else: # windows will need to be handled separately for Ctrl+C @@ -541,7 +557,11 @@ class Server: while running: x = x + 1 - logstr = f"Server {server_name} is still running - waiting 2s to see if it stops ({int(60-(x*2))} seconds until force close)" + logstr = ( + f"Server {server_name} is still running " + f"- waiting 2s to see if it stops ({int(60-(x*2))} " + f"seconds until force close)" + ) logger.info(logstr) console.info(logstr) running = self.check_running() @@ -624,19 +644,23 @@ class Server: if self.settings["crash_detection"]: logger.warning( - f"The server {name} has crashed and will be restarted. Restarting server" + f"The server {name} has crashed and will be restarted. " + f"Restarting server" ) console.warning( - f"The server {name} has crashed and will be restarted. Restarting server" + f"The server {name} has crashed and will be restarted. " + f"Restarting server" ) self.run_threaded_server(None) return True else: logger.critical( - f"The server {name} has crashed, crash detection is disabled and it will not be restarted" + f"The server {name} has crashed, " + f"crash detection is disabled and it will not be restarted" ) console.critical( - f"The server {name} has crashed, crash detection is disabled and it will not be restarted" + f"The server {name} has crashed, " + f"crash detection is disabled and it will not be restarted" ) return False @@ -646,7 +670,7 @@ class Server: # for every sub process... for proc in process.children(recursive=True): - # kill all the child processes - it sounds too wrong saying kill all the children (kevdagoat: lol!) + # kill all the child processes logger.info(f"Sending SIGKILL to server {proc.name}") proc.kill() # kill the main process we are after @@ -678,8 +702,9 @@ class Server: # check the exit code -- This could be a fix for /stop if self.process.returncode == 0: logger.warning( - f"Process {self.process.pid} exited with code {self.process.returncode}. This is considered a clean exit" - + " supressing crash handling." + f"Process {self.process.pid} exited with code " + f"{self.process.returncode}. This is considered a clean exit" + f" supressing crash handling." ) # cancel the watcher task self.server_scheduler.remove_job("c_" + str(self.server_id)) @@ -699,10 +724,12 @@ class Server: # we have tried to restart 4 times... elif self.restart_count == 4: logger.critical( - f"Server {self.name} has been restarted {self.restart_count} times. It has crashed, not restarting." + f"Server {self.name} has been restarted {self.restart_count}" + f" times. It has crashed, not restarting." ) console.critical( - f"Server {self.name} has been restarted {self.restart_count} times. It has crashed, not restarting." + f"Server {self.name} has been restarted {self.restart_count}" + f" times. It has crashed, not restarting." ) self.restart_count = 0 @@ -740,7 +767,8 @@ class Server: if self.server_path is None: self.server_path = helper.get_os_understandable_path(self.settings["path"]) logger.info( - "Backup Thread - Local server path not defined. Setting local server path variable." + "Backup Thread - Local server path not defined. " + "Setting local server path variable." ) # checks if the backup thread is currently alive for this server if not self.is_backingup: @@ -752,7 +780,8 @@ class Server: return False else: logger.error( - f"Backup is already being processed for server {self.settings['server_name']}. Canceling backup request" + f"Backup is already being processed for server " + f"{self.settings['server_name']}. Canceling backup request" ) return False logger.info(f"Backup Thread started for server {self.settings['server_name']}.") @@ -779,10 +808,14 @@ class Server: conf = management_helper.get_backup_config(self.server_id) helper.ensure_dir_exists(self.settings["backup_path"]) try: - backup_filename = f"{self.settings['backup_path']}/{datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}" + backup_filename = ( + f"{self.settings['backup_path']}/" + f"{datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}" + ) logger.info( f"Creating backup of server '{self.settings['server_name']}'" - + f" (ID#{self.server_id}, path={self.server_path}) at '{backup_filename}'" + f" (ID#{self.server_id}, path={self.server_path}) " + f"at '{backup_filename}'" ) tempDir = tempfile.mkdtemp() @@ -799,14 +832,17 @@ class Server: server_dir = helper.get_os_understandable_path(self.settings["path"]) for my_dir in excluded_dirs: - # Take the full path of the excluded dir and replace the server path with the temp path - # This is so that we're only deleting excluded dirs from the temp path and not the server path + # Take the full path of the excluded dir and replace the + # server path with the temp path, this is so that we're + # only deleting excluded dirs from the temp path + # and not the server path excluded_dir = helper.get_os_understandable_path(my_dir).replace( server_dir, helper.get_os_understandable_path(tempDir) ) # Next, check to see if it is a directory if os.path.isdir(excluded_dir): - # If it is a directory, recursively delete the entire directory from the backup + # If it is a directory, + # recursively delete the entire directory from the backup file_helper.del_dirs(excluded_dir) else: # If not, just remove the file @@ -945,7 +981,8 @@ class Server: if self.check_running(): wasStarted = True logger.info( - f"Server with PID {self.process.pid} is running. Sending shutdown command" + f"Server with PID {self.process.pid} is running. " + f"Sending shutdown command" ) self.stop_threaded_server() else: @@ -976,7 +1013,8 @@ class Server: backup_executable = os.path.join(backup_dir, "old_server.jar") else: logger.info( - f"Executable backup directory not found for Server: {self.name}. Creating one." + f"Executable backup directory not found for Server: {self.name}." + f" Creating one." ) os.mkdir(backup_dir) backup_executable = os.path.join(backup_dir, "old_server.jar") @@ -1062,9 +1100,9 @@ class Server: ) logger.error("Executable download failed.") - # ************************************************************************************************ + # ********************************************************************************** # Minecraft Servers Statistics - # ************************************************************************************************ + # ********************************************************************************** def realtime_stats(self): total_players = 0 @@ -1180,7 +1218,8 @@ class Server: ping_data = Stats.parse_server_RakNet_ping(int_mc_ping) else: ping_data = Stats.parse_server_ping(int_mc_ping) - # Makes sure we only show stats when a server is online otherwise people have gotten confused. + # Makes sure we only show stats when a server is online + # otherwise people have gotten confused. if self.check_running(): server_stats = { "id": server_id, @@ -1302,7 +1341,8 @@ class Server: int_data = False ping_data = {} - # Makes sure we only show stats when a server is online otherwise people have gotten confused. + # Makes sure we only show stats when a server is online + # otherwise people have gotten confused. if self.check_running(): # if we got a good ping return, let's parse it if servers_helper.get_server_type_by_id(server_id) != "minecraft-bedrock": diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index 1b9f5db9..eef4ba73 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -92,7 +92,8 @@ class TasksManager: svr = self.controller.get_server_obj(c.server_id) except: logger.error( - "Server value requested does note exist purging item from waiting commands." + "Server value requested does note exist! " + "Purging item from waiting commands." ) management_helper.mark_command_complete(c.command_id) @@ -162,7 +163,9 @@ class TasksManager: def scheduler_thread(self): schedules = management_helper.get_schedules_enabled() self.scheduler.add_listener(self.schedule_watcher, mask=EVENT_JOB_EXECUTED) - # self.scheduler.add_job(self.scheduler.print_jobs, 'interval', seconds=10, id='-1') + # self.scheduler.add_job( + # self.scheduler.print_jobs, "interval", seconds=10, id="-1" + # ) # load schedules from DB for schedule in schedules: @@ -254,7 +257,8 @@ class TasksManager: job_data["parent"], job_data["delay"], ) - # Checks to make sure some doofus didn't actually make the newly created task a child of itself. + # Checks to make sure some doofus didn't actually make the newly + # created task a child of itself. if str(job_data["parent"]) == str(sch_id): management_helper.update_scheduled_task(sch_id, {"parent": None}) # Check to see if it's enabled and is not a chain reaction. @@ -349,12 +353,14 @@ class TasksManager: else: logger.info( f"Job with ID {sch_id} was deleted from DB, but was not enabled." - + "Not going to try removing something that doesn't exist from active schedules." + f"Not going to try removing something " + f"that doesn't exist from active schedules." ) def update_job(self, sch_id, job_data): management_helper.update_scheduled_task(sch_id, job_data) - # Checks to make sure some doofus didn't actually make the newly created task a child of itself. + # Checks to make sure some doofus didn't actually make the newly + # created task a child of itself. if str(job_data["parent"]) == str(sch_id): management_helper.update_scheduled_task(sch_id, {"parent": None}) try: @@ -362,7 +368,8 @@ class TasksManager: self.scheduler.remove_job(str(sch_id)) except: logger.info( - "No job found in update job. Assuming it was previously disabled. Starting new job." + "No job found in update job. " + "Assuming it was previously disabled. Starting new job." ) if job_data["enabled"]: @@ -436,7 +443,8 @@ class TasksManager: self.scheduler.remove_job(str(sch_id)) except: logger.info( - f"APScheduler found no scheduled job on schedule update for schedule with id: {sch_id} Assuming it was already disabled." + f"APScheduler found no scheduled job on schedule update for " + f"schedule with id: {sch_id} Assuming it was already disabled." ) def schedule_watcher(self, event): @@ -454,11 +462,14 @@ class TasksManager: if task.one_time: self.remove_job(task.schedule_id) logger.info("one time task detected. Deleting...") - # check for any child tasks for this. It's kind of backward, but this makes DB management a lot easier. One to one instead of one to many. + # check for any child tasks for this. It's kind of backward, + # but this makes DB management a lot easier. One to one + # instead of one to many. for schedule in management_helper.get_child_schedules_by_server( task.schedule_id, task.server_id ): - # event job ID's are strings so we need to look at this as the same data type. + # event job ID's are strings so we need to look at + # this as the same data type. if str(schedule.parent) == str(event.job_id): if schedule.enabled: delaytime = datetime.datetime.now() + datetime.timedelta( @@ -478,7 +489,8 @@ class TasksManager: ) else: logger.info( - "Event job ID is not numerical. Assuming it's stats - not stored in DB. Moving on." + "Event job ID is not numerical. Assuming it's stats " + "- not stored in DB. Moving on." ) else: logger.error(f"Task failed with error: {event.exception}") diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py index 77b79828..eca70e88 100644 --- a/app/classes/web/ajax_handler.py +++ b/app/classes/web/ajax_handler.py @@ -173,7 +173,8 @@ class AjaxHandler(BaseHandler): data-path="{dpath}" data-name="{filename}" onclick=""> - {filename}""" + + {filename}""" self.write(helper.get_os_understandable_path(folder) + "\n" + output) self.finish() @@ -236,7 +237,8 @@ class AjaxHandler(BaseHandler): data-path="{dpath}" data-name="{filename}" onclick=""> - {filename}""" + + {filename}""" self.write(helper.get_os_understandable_path(folder) + "\n" + output) self.finish() @@ -320,7 +322,9 @@ class AjaxHandler(BaseHandler): self.controller.management.add_to_audit_log( exec_user["user_id"], - f"Sent command to {self.controller.servers.get_server_friendly_name(server_id)} terminal: {command}", + f"Sent command to " + f"{self.controller.servers.get_server_friendly_name(server_id)} " + f"terminal: {command}", server_id, self.get_remote_ip(), ) @@ -516,7 +520,8 @@ class AjaxHandler(BaseHandler): return server_id = self.get_argument("id", None) logger.info( - f"Removing server from panel for server: {self.controller.servers.get_server_friendly_name(server_id)}" + f"Removing server from panel for server: " + f"{self.controller.servers.get_server_friendly_name(server_id)}" ) server_data = self.controller.get_server_data(server_id) @@ -539,7 +544,8 @@ class AjaxHandler(BaseHandler): return server_id = self.get_argument("id", None) logger.info( - f"Removing server and all associated files for server: {self.controller.servers.get_server_friendly_name(server_id)}" + f"Removing server and all associated files for server: " + f"{self.controller.servers.get_server_friendly_name(server_id)}" ) server_data = self.controller.get_server_data(server_id) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index fd859ae1..93ec83a9 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -1,3 +1,4 @@ +# pylint: disable=too-many-lines import time import datetime import os @@ -36,7 +37,8 @@ class PanelHandler(BaseHandler): user_roles = {} for user in self.controller.users.get_all_users(): user_roles_list = self.controller.users.get_user_roles_names(user.user_id) - # user_servers = self.controller.servers.get_authorized_servers(user.user_id) + # user_servers = + # self.controller.servers.get_authorized_servers(user.user_id) user_roles[user.user_id] = user_roles_list return user_roles @@ -142,7 +144,8 @@ class PanelHandler(BaseHandler): api_key, _, exec_user = self.current_user superuser = exec_user["superuser"] - # Commented out because there is no server access control for API keys, they just inherit from the host user + # Commented out because there is no server access control for API keys, + # they just inherit from the host user # if api_key is not None: # superuser = superuser and api_key.superuser @@ -162,7 +165,8 @@ class PanelHandler(BaseHandler): server_id, api_key ): print( - f"API key {api_key.name} (id: {api_key.token_id}) does not have permission" + f"API key {api_key.name} (id: {api_key.token_id}) " + f"does not have permission" ) self.redirect("/panel/error?error=Invalid Server ID") return None @@ -434,7 +438,8 @@ class PanelHandler(BaseHandler): page_servers.append(server) un_used_servers.remove(server) user_order.remove(server_id) - # we only want to set these server stats values once. We need to update the flag so it only hits that if once. + # we only want to set these server stats values once. + # We need to update the flag so it only hits that if once. flag += 1 for server in un_used_servers: @@ -447,7 +452,8 @@ class PanelHandler(BaseHandler): user_order.remove(server_id) page_data["servers"] = page_servers - # num players is set to zero here. If we poll all servers while dashboard is loading it takes FOREVER. We leave this to the + # num players is set to zero here. If we poll all servers while + # dashboard is loading it takes FOREVER. We leave this to the # async polling once dashboard is served. page_data["num_players"] = 0 @@ -778,7 +784,7 @@ class PanelHandler(BaseHandler): page_data[ "quantity_server" ] = ( - self.controller.crafty_perms.list_all_crafty_permissions_quantity_limits() + self.controller.crafty_perms.list_all_crafty_permissions_quantity_limits() # pylint: disable=line-too-long ) page_data["languages"] = [] page_data["languages"].append( @@ -843,7 +849,8 @@ class PanelHandler(BaseHandler): page_data["schedule"]["cron_string"] = "" page_data["schedule"]["time"] = "" page_data["schedule"]["interval"] = "" - # we don't need to check difficulty here. We'll just default to basic for new schedules + # we don't need to check difficulty here. + # We'll just default to basic for new schedules page_data["schedule"]["difficulty"] = "basic" page_data["schedule"]["interval_type"] = "days" @@ -898,7 +905,8 @@ class PanelHandler(BaseHandler): "children" ] = self.controller.management.get_child_schedules(sch_id) # We check here to see if the command is any of the default ones. - # We do not want a user changing to a custom command and seeing our command there. + # We do not want a user changing to a custom command + # and seeing our command there. if ( schedule.action != "start" or schedule.action != "stop" @@ -1192,7 +1200,8 @@ class PanelHandler(BaseHandler): elif page == "support_logs": logger.info( - f"Support logs requested. Packinging logs for user with ID: {exec_user['user_id']}" + f"Support logs requested. " + f"Packinging logs for user with ID: {exec_user['user_id']}" ) logs_thread = threading.Thread( target=self.controller.package_support_logs, @@ -1243,7 +1252,8 @@ class PanelHandler(BaseHandler): exec_user["user_id"] ) ) - # defined_servers = self.controller.servers.get_authorized_servers(exec_user["user_id"]) + # defined_servers = + # self.controller.servers.get_authorized_servers(exec_user["user_id"]) for r in exec_user["roles"]: role = self.controller.roles.get_role(r) exec_user_role.add(role["role_name"]) @@ -1289,7 +1299,8 @@ class PanelHandler(BaseHandler): server_obj = self.controller.servers.get_server_obj(server_id) stale_executable = server_obj.executable - # Compares old jar name to page data being passed. If they are different we replace the executable name in the + # Compares old jar name to page data being passed. + # If they are different we replace the executable name in the if str(stale_executable) != str(executable): execution_command = execution_command.replace( str(stale_executable), str(executable) @@ -1711,7 +1722,8 @@ class PanelHandler(BaseHandler): elif page == "edit_user": if bleach.clean(self.get_argument("username", None)) == "system": self.redirect( - "/panel/error?error=Unauthorized access: system user is not editable" + "/panel/error?error=Unauthorized access: " + "system user is not editable" ) user_id = bleach.clean(self.get_argument("id", None)) username = bleach.clean(self.get_argument("username", None)) @@ -1724,8 +1736,9 @@ class PanelHandler(BaseHandler): ) if superuser: - # Checks if user is trying to change super user status of self. We don't want that. - # Automatically make them stay super user since we know they are. + # Checks if user is trying to change super user status of self. + # We don't want that. Automatically make them stay super user + # since we know they are. if str(exec_user["user_id"]) != str(user_id): superuser = bleach.clean(self.get_argument("superuser", "0")) else: @@ -1800,7 +1813,8 @@ class PanelHandler(BaseHandler): self.controller.management.add_to_audit_log( exec_user["user_id"], - f"Edited user {username} (UID:{user_id}) with roles {roles} and permissions {permissions_mask}", + f"Edited user {username} (UID:{user_id}) with roles {roles} " + f"and permissions {permissions_mask}", server_id=0, source_ip=self.get_remote_ip(), ) @@ -1836,8 +1850,9 @@ class PanelHandler(BaseHandler): self.controller.management.add_to_audit_log( exec_user["user_id"], - f"Added API key {name} with crafty permissions {crafty_permissions_mask}" - + f" and {server_permissions_mask} for user with UID: {user_id}", + f"Added API key {name} with crafty permissions " + f"{crafty_permissions_mask}" + f" and {server_permissions_mask} for user with UID: {user_id}", server_id=0, source_ip=self.get_remote_ip(), ) @@ -1858,7 +1873,8 @@ class PanelHandler(BaseHandler): self.controller.management.add_to_audit_log( exec_user["user_id"], - f"Generated a new API token for the key {key.name} from user with UID: {key.user.user_id}", + f"Generated a new API token for the key {key.name} " + f"from user with UID: {key.user.user_id}", server_id=0, source_ip=self.get_remote_ip(), ) @@ -1871,8 +1887,9 @@ class PanelHandler(BaseHandler): elif page == "add_user": if bleach.clean(self.get_argument("username", None)).lower() == "system": self.redirect( - "/panel/error?error=Unauthorized access: username system is reserved for the Crafty system." - + " Please choose a different username." + "/panel/error?error=Unauthorized access: " + "username system is reserved for the Crafty system." + " Please choose a different username." ) return username = bleach.clean(self.get_argument("username", None)) @@ -2074,7 +2091,8 @@ class PanelHandler(BaseHandler): self.controller.management.add_to_audit_log( exec_user["user_id"], - f"Removed API key {target_key} (ID: {key_id}) from user {exec_user['user_id']}", + f"Removed API key {target_key} " + f"(ID: {key_id}) from user {exec_user['user_id']}", server_id=0, source_ip=self.get_remote_ip(), ) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index f4d392de..a91482ef 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -127,7 +127,10 @@ class PublicHandler(BaseHandler): # if they are disabled if not user_data.enabled: - error_msg = "User account disabled. Please contact your system administrator for more info." + error_msg = ( + "User account disabled. Please contact " + "your system administrator for more info." + ) # self.clear_cookie("user") # self.clear_cookie("user_data") self.clear_cookie("token") diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index feb98075..d6507c24 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -126,7 +126,8 @@ class ServerHandler(BaseHandler): exec_user["user_id"] ): self.redirect( - "/panel/error?error=Unauthorized access: not a server creator or server limit reached" + "/panel/error?error=Unauthorized access: " + "not a server creator or server limit reached" ) return @@ -141,7 +142,8 @@ class ServerHandler(BaseHandler): exec_user["user_id"] ): self.redirect( - "/panel/error?error=Unauthorized access: not a server creator or server limit reached" + "/panel/error?error=Unauthorized access: " + "not a server creator or server limit reached" ) return @@ -286,7 +288,7 @@ class ServerHandler(BaseHandler): ) self.controller.management.add_to_audit_log( exec_user["user_id"], - f'imported a jar server named "{server_name}"', # Example: Admin imported a server named "old creative" + f'imported a jar server named "{server_name}"', new_server_id, self.get_remote_ip(), ) @@ -303,13 +305,15 @@ class ServerHandler(BaseHandler): ) if new_server_id == "false": self.redirect( - "/panel/error?error=Zip file not accessible! You can fix this permissions issue with" - + f"sudo chown -R crafty:crafty {import_server_path} And sudo chmod 2775 -R {import_server_path}" + f"/panel/error?error=Zip file not accessible! " + f"You can fix this permissions issue with " + f"sudo chown -R crafty:crafty {import_server_path} " + f"And sudo chmod 2775 -R {import_server_path}" ) return self.controller.management.add_to_audit_log( exec_user["user_id"], - f'imported a zip server named "{server_name}"', # Example: Admin imported a server named "old creative" + f'imported a zip server named "{server_name}"', new_server_id, self.get_remote_ip(), ) @@ -320,20 +324,23 @@ class ServerHandler(BaseHandler): self.redirect("/panel/error?error=Invalid server data") return server_type, server_version = server_parts - # TODO: add server type check here and call the correct server add functions if not a jar + # TODO: add server type check here and call the correct server + # add functions if not a jar role_ids = self.controller.users.get_user_roles_id(exec_user["user_id"]) new_server_id = self.controller.create_jar_server( server_type, server_version, server_name, min_mem, max_mem, port ) self.controller.management.add_to_audit_log( exec_user["user_id"], - f'created a {server_version} {str(server_type).capitalize()} server named "{server_name}"', + f"created a {server_version} {str(server_type).capitalize()}" + f' server named "{server_name}"', # Example: Admin created a 1.16.5 Bukkit server named "survival" new_server_id, self.get_remote_ip(), ) - # These lines create a new Role for the Server with full permissions and add the user to it if he's not a superuser + # These lines create a new Role for the Server with full permissions + # and add the user to it if he's not a superuser if len(captured_roles) == 0: if not superuser: new_server_uuid = self.controller.servers.get_server_data_by_id( @@ -399,7 +406,7 @@ class ServerHandler(BaseHandler): ) self.controller.management.add_to_audit_log( exec_user["user_id"], - f'imported a jar server named "{server_name}"', # Example: Admin imported a server named "old creative" + f'imported a jar server named "{server_name}"', new_server_id, self.get_remote_ip(), ) @@ -416,13 +423,15 @@ class ServerHandler(BaseHandler): ) if new_server_id == "false": self.redirect( - "/panel/error?error=Zip file not accessible! You can fix this permissions issue with" - + f"sudo chown -R crafty:crafty {import_server_path} And sudo chmod 2775 -R {import_server_path}" + f"/panel/error?error=Zip file not accessible! " + f"You can fix this permissions issue with" + f"sudo chown -R crafty:crafty {import_server_path} " + f"And sudo chmod 2775 -R {import_server_path}" ) return self.controller.management.add_to_audit_log( exec_user["user_id"], - f'imported a zip server named "{server_name}"', # Example: Admin imported a server named "old creative" + f'imported a zip server named "{server_name}"', new_server_id, self.get_remote_ip(), ) @@ -433,20 +442,23 @@ class ServerHandler(BaseHandler): self.redirect("/panel/error?error=Invalid server data") return server_type, server_version = server_parts - # TODO: add server type check here and call the correct server add functions if not a jar + # TODO: add server type check here and call the correct server + # add functions if not a jar role_ids = self.controller.users.get_user_roles_id(exec_user["user_id"]) new_server_id = self.controller.create_jar_server( server_type, server_version, server_name, min_mem, max_mem, port ) self.controller.management.add_to_audit_log( exec_user["user_id"], - f'created a {server_version} {str(server_type).capitalize()} server named "{server_name}"', + f"created a {server_version} {str(server_type).capitalize()} " + f'server named "{server_name}"', # Example: Admin created a 1.16.5 Bukkit server named "survival" new_server_id, self.get_remote_ip(), ) - # These lines create a new Role for the Server with full permissions and add the user to it if he's not a superuser + # These lines create a new Role for the Server with full permissions + # and add the user to it if he's not a superuser if len(captured_roles) == 0: if not superuser: new_server_uuid = self.controller.servers.get_server_data_by_id( diff --git a/app/classes/web/tornado_handler.py b/app/classes/web/tornado_handler.py index ad62629c..805820ac 100644 --- a/app/classes/web/tornado_handler.py +++ b/app/classes/web/tornado_handler.py @@ -61,12 +61,15 @@ class Webserver: @staticmethod def _asyncio_patch(): """ - As of Python 3.8 (on Windows), the asyncio default event handler has changed to "proactor", + As of Python 3.8 (on Windows), + the asyncio default event handler has changed to "proactor", where tornado expects the "selector" handler. - This function checks if the platform is windows and changes the event handler to suit. + This function checks if the platform is windows and + changes the event handler to suit. - (Taken from https://github.com/mkdocs/mkdocs/commit/cf2b136d4257787c0de51eba2d9e30ded5245b31) + (Taken from + https://github.com/mkdocs/mkdocs/commit/cf2b136d4257787c0de51eba2d9e30ded5245b31) """ logger.debug("Checking if asyncio patch is required") if sys.platform.startswith("win") and sys.version_info >= (3, 8): @@ -189,10 +192,12 @@ class Webserver: self.HTTPS_Server.listen(https_port) logger.info( - f"https://{helper.get_local_ip()}:{https_port} is up and ready for connections." + f"https://{helper.get_local_ip()}:{https_port} " + f"is up and ready for connections." ) console.info( - f"https://{helper.get_local_ip()}:{https_port} is up and ready for connections." + f"https://{helper.get_local_ip()}:{https_port} " + f"is up and ready for connections." ) console.info("Server Init Complete: Listening For Connections:") diff --git a/app/classes/web/upload_handler.py b/app/classes/web/upload_handler.py index 9a6e71a0..5ebcfff0 100644 --- a/app/classes/web/upload_handler.py +++ b/app/classes/web/upload_handler.py @@ -75,10 +75,12 @@ class UploadHandler(BaseHandler): if Enum_Permissions_Server.Files not in exec_user_server_permissions: logger.warning( - f"User {user_id} tried to upload a file to {server_id} without permissions!" + f"User {user_id} tried to upload a file to " + f"{server_id} without permissions!" ) console.warning( - f"User {user_id} tried to upload a file to {server_id} without permissions!" + f"User {user_id} tried to upload a file to " + f"{server_id} without permissions!" ) self.do_upload = False @@ -101,10 +103,12 @@ class UploadHandler(BaseHandler): full_path, ) logger.warning( - f"User {user_id} tried to upload a file to {server_id} but the path is not inside of the server!" + f"User {user_id} tried to upload a file to {server_id} " + f"but the path is not inside of the server!" ) console.warning( - f"User {user_id} tried to upload a file to {server_id} but the path is not inside of the server!" + f"User {user_id} tried to upload a file to {server_id} " + f"but the path is not inside of the server!" ) self.do_upload = False diff --git a/app/classes/web/websocket_helper.py b/app/classes/web/websocket_helper.py index 9d39659b..5e4f3289 100644 --- a/app/classes/web/websocket_helper.py +++ b/app/classes/web/websocket_helper.py @@ -24,14 +24,16 @@ class WebSocketHelper: def broadcast(self, event_type: str, data): logger.debug( - f"Sending to {len(self.clients)} clients: {json.dumps({'event': event_type, 'data': data})}" + f"Sending to {len(self.clients)} clients: " + f"{json.dumps({'event': event_type, 'data': data})}" ) for client in self.clients: try: self.send_message(client, event_type, data) except Exception as e: logger.exception( - f"Error caught while sending WebSocket message to {client.get_remote_ip()} {e}" + f"Error caught while sending WebSocket message to " + f"{client.get_remote_ip()} {e}" ) def broadcast_page(self, page: str, event_type: str, data): @@ -85,7 +87,8 @@ class WebSocketHelper: def broadcast_with_fn(self, filter_fn, event_type: str, data): clients = list(filter(filter_fn, self.clients)) logger.debug( - f"Sending to {len(clients)} out of {len(self.clients)} clients: {json.dumps({'event': event_type, 'data': data})}" + f"Sending to {len(clients)} out of {len(self.clients)} " + f"clients: {json.dumps({'event': event_type, 'data': data})}" ) for client in clients: @@ -93,7 +96,8 @@ class WebSocketHelper: self.send_message(client, event_type, data) except Exception as e: logger.exception( - f"Error catched while sending WebSocket message to {client.get_remote_ip()} {e}" + f"Error catched while sending WebSocket message to " + f"{client.get_remote_ip()} {e}" ) def disconnect_all(self): From 09bba7fdb0760fff8f7b61a082f2bcebbb517dea Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 23 Mar 2022 06:16:22 +0000 Subject: [PATCH 13/16] =?UTF-8?q?Further=20fix=20files=20to=20conform=20wi?= =?UTF-8?q?th=20=E2=9A=ABBlack=20pylintrc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mostly just breaking up strings and comments into new lines Some strings dont require 'f' but keeping in for readability with the rest of the concatinated string --- app/classes/models/management.py | 6 +- app/classes/models/users.py | 3 +- app/classes/shared/translation.py | 180 +++++++++++++++--------------- main.py | 20 ++-- 4 files changed, 111 insertions(+), 98 deletions(-) diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 039ceeeb..df25e5a0 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -417,7 +417,8 @@ class helpers_management: self.set_backup_config(server_id=server_id, excluded_dirs=excluded_dirs) else: logger.debug( - f"Not adding {dir_to_add} to excluded directories - already in the excluded directory list for server ID {server_id}" + f"Not adding {dir_to_add} to excluded directories - " + f"already in the excluded directory list for server ID {server_id}" ) def del_excluded_backup_dir(self, server_id: int, dir_to_del: str): @@ -428,7 +429,8 @@ class helpers_management: self.set_backup_config(server_id=server_id, excluded_dirs=excluded_dirs) else: logger.debug( - f"Not removing {dir_to_del} from excluded directories - not in the excluded directory list for server ID {server_id}" + f"Not removing {dir_to_del} from excluded directories - " + f"not in the excluded directory list for server ID {server_id}" ) @staticmethod diff --git a/app/classes/models/users.py b/app/classes/models/users.py index a78862ba..dbe189c7 100644 --- a/app/classes/models/users.py +++ b/app/classes/models/users.py @@ -297,7 +297,8 @@ class helper_users: else: user_id = user.user_id - # I just copied this code from get_user, it had those TODOs & comments made by mac - Lukas + # I just copied this code from get_user, + # it had those TODOs & comments made by mac - Lukas roles_query = ( User_Roles.select() diff --git a/app/classes/shared/translation.py b/app/classes/shared/translation.py index d2e98573..22ee1d81 100644 --- a/app/classes/shared/translation.py +++ b/app/classes/shared/translation.py @@ -1,88 +1,92 @@ -import json -import logging -import os -import typing as t - -from app.classes.shared.console import console -from app.classes.shared.helpers import helper - -logger = logging.getLogger(__name__) - - -class Translation: - def __init__(self): - self.translations_path = os.path.join(helper.root_dir, "app", "translations") - self.cached_translation = None - self.cached_translation_lang = None - - def get_language_file(self, language: str): - return os.path.join(self.translations_path, str(language) + ".json") - - def translate(self, page, word, language): - fallback_language = "en_EN" - - translated_word = self.translate_inner(page, word, language) - if translated_word is None: - translated_word = self.translate_inner(page, word, fallback_language) - - if translated_word: - if isinstance(translated_word, dict): - # JSON objects - return json.dumps(translated_word) - elif isinstance(translated_word, str): - # Basic strings - return translated_word - elif hasattr(translated_word, "__iter__"): - # Multiline strings - return "\n".join(translated_word) - return "Error while getting translation" - - def translate_inner(self, page, word, language) -> t.Union[t.Any, None]: - language_file = self.get_language_file(language) - try: - if not self.cached_translation: - with open(language_file, "r", encoding="utf-8") as f: - data = json.load(f) - self.cached_translation = data - elif self.cached_translation_lang != language: - with open(language_file, "r", encoding="utf-8") as f: - data = json.load(f) - self.cached_translation = data - self.cached_translation_lang = language - else: - data = self.cached_translation - - try: - translated_page = data[page] - except KeyError: - logger.error( - f"Translation File Error: page {page} does not exist for lang {language}" - ) - console.error( - f"Translation File Error: page {page} does not exist for lang {language}" - ) - return None - - try: - translated_word = translated_page[word] - return translated_word - except KeyError: - logger.error( - f"Translation File Error: word {word} does not exist on page {page} for lang {language}" - ) - console.error( - f"Translation File Error: word {word} does not exist on page {page} for lang {language}" - ) - return None - - except Exception as e: - logger.critical( - f"Translation File Error: Unable to read {language_file} due to {e}" - ) - console.critical( - f"Translation File Error: Unable to read {language_file} due to {e}" - ) - return None - - -translation = Translation() +import json +import logging +import os +import typing as t + +from app.classes.shared.console import console +from app.classes.shared.helpers import helper + +logger = logging.getLogger(__name__) + + +class Translation: + def __init__(self): + self.translations_path = os.path.join(helper.root_dir, "app", "translations") + self.cached_translation = None + self.cached_translation_lang = None + + def get_language_file(self, language: str): + return os.path.join(self.translations_path, str(language) + ".json") + + def translate(self, page, word, language): + fallback_language = "en_EN" + + translated_word = self.translate_inner(page, word, language) + if translated_word is None: + translated_word = self.translate_inner(page, word, fallback_language) + + if translated_word: + if isinstance(translated_word, dict): + # JSON objects + return json.dumps(translated_word) + elif isinstance(translated_word, str): + # Basic strings + return translated_word + elif hasattr(translated_word, "__iter__"): + # Multiline strings + return "\n".join(translated_word) + return "Error while getting translation" + + def translate_inner(self, page, word, language) -> t.Union[t.Any, None]: + language_file = self.get_language_file(language) + try: + if not self.cached_translation: + with open(language_file, "r", encoding="utf-8") as f: + data = json.load(f) + self.cached_translation = data + elif self.cached_translation_lang != language: + with open(language_file, "r", encoding="utf-8") as f: + data = json.load(f) + self.cached_translation = data + self.cached_translation_lang = language + else: + data = self.cached_translation + + try: + translated_page = data[page] + except KeyError: + logger.error( + f"Translation File Error: page {page} " + f"does not exist for lang {language}" + ) + console.error( + f"Translation File Error: page {page} " + f"does not exist for lang {language}" + ) + return None + + try: + translated_word = translated_page[word] + return translated_word + except KeyError: + logger.error( + f"Translation File Error: word {word} does not exist on page " + f"{page} for lang {language}" + ) + console.error( + f"Translation File Error: word {word} does not exist on page " + f"{page} for lang {language}" + ) + return None + + except Exception as e: + logger.critical( + f"Translation File Error: Unable to read {language_file} due to {e}" + ) + console.critical( + f"Translation File Error: Unable to read {language_file} due to {e}" + ) + return None + + +translation = Translation() diff --git a/main.py b/main.py index 41ea9f53..0ce9e31a 100644 --- a/main.py +++ b/main.py @@ -10,7 +10,8 @@ from app.classes.shared.helpers import helper if helper.checkRoot(): console.critical( - "Root detected. Root/Admin access denied. Run Crafty again with non-elevated permissions." + "Root detected. Root/Admin access denied. " + "Run Crafty again with non-elevated permissions." ) time.sleep(5) console.critical("Crafty shutting down. Root/Admin access denied.") @@ -103,8 +104,10 @@ if __name__ == "__main__": if fresh_install: console.debug("Fresh install detected") console.warning( - "We have detected a fresh install. Please be sure to forward Crafty's port, " - + f"{helper.get_setting('https_port')}, through your router/firewall if you would like to be able to access Crafty remotely." + f"We have detected a fresh install. Please be sure to forward " + f"Crafty's port, {helper.get_setting('https_port')}, " + f"through your router/firewall if you would like to be able " + f"to access Crafty remotely." ) installer.default_settings() else: @@ -127,10 +130,12 @@ if __name__ == "__main__": # start stats logging tasks_manager.start_stats_recording() - # once the controller is up and stats are logging, we can kick off the scheduler officially + # once the controller is up and stats are logging, we can kick off + # the scheduler officially tasks_manager.start_scheduler() - # refresh our cache and schedule for every 12 hoursour cache refresh for serverjars.com + # refresh our cache and schedule for every 12 hoursour cache refresh + # for serverjars.com tasks_manager.serverjar_cache_refresher() logger.info("Checking Internet. This may take a minute.") @@ -138,8 +143,9 @@ if __name__ == "__main__": if not helper.check_internet(): console.warning( - "We have detected the machine running Crafty has no connection to the internet. " - + "Client connections to the server may be limited." + "We have detected the machine running Crafty has no " + "connection to the internet. Client connections to " + "the server may be limited." ) if not controller.check_system_user(): From 237e43fa751911ad708ee02e209ac3f575a25b6c Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 23 Mar 2022 06:25:44 +0000 Subject: [PATCH 14/16] =?UTF-8?q?Update=20readme=20to=20add=20=E2=AC=9BBla?= =?UTF-8?q?ck=20Badge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5ca64a94..960f6389 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) # Crafty Controller 4.0.0-alpha.3.5 > Python based Control Panel for your Minecraft Server From cd87aa6a0491d9b9c4a702386ca42e0bda945df6 Mon Sep 17 00:00:00 2001 From: Iain Powrie Date: Wed, 23 Mar 2022 11:11:59 +0000 Subject: [PATCH 15/16] Preemptively at badges Hardcoded badges will be replaced with generated ones when the repository is public --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 960f6389..979471d6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![Supported Python Versions](https://shields.io/badge/python-3.7%20%7C%203.8%20%7C%203.9-blue)](https://gitlab.com/crafty-controller/crafty-commander) +[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.0--alpha3.5-orange)](https://gitlab.com/crafty-controller/crafty-commander) +[![Code Quality(temp-hardcoded)](https://img.shields.io/badge/code%20quality-10-brightgreen)](https://gitlab.com/crafty-controller/crafty-commander) +[![Build Status](https://gitlab.com/crafty-controller/crafty-commander/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-commander/-/commits/master) # Crafty Controller 4.0.0-alpha.3.5 > Python based Control Panel for your Minecraft Server From e9ad7696a5535824a232611c0c24059de39c1704 Mon Sep 17 00:00:00 2001 From: Iain Powrie Date: Wed, 23 Mar 2022 12:29:46 +0000 Subject: [PATCH 16/16] Add logo svg to Repo Waiting for review, it's pretty :) --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 979471d6..5c3db87f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ +[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com) + [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Supported Python Versions](https://shields.io/badge/python-3.7%20%7C%203.8%20%7C%203.9-blue)](https://gitlab.com/crafty-controller/crafty-commander) [![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.0--alpha3.5-orange)](https://gitlab.com/crafty-controller/crafty-commander) [![Code Quality(temp-hardcoded)](https://img.shields.io/badge/code%20quality-10-brightgreen)](https://gitlab.com/crafty-controller/crafty-commander) [![Build Status](https://gitlab.com/crafty-controller/crafty-commander/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-commander/-/commits/master) + # Crafty Controller 4.0.0-alpha.3.5 > Python based Control Panel for your Minecraft Server