diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1996376a..19b94a44 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,5 @@
stages:
+- test
- prod-deployment
- dev-deployment
@@ -6,6 +7,36 @@ variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
+pylint:
+ stage: test
+ image: python:3.7-slim
+ services:
+ - name: docker:dind
+ tags:
+ - 'docker_testers'
+ rules:
+ - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
+ - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
+ when: never
+ before_script:
+ - mkdir -p public/badges public/lint
+ - echo undefined > public/badges/$CI_JOB_NAME.score
+ - pip install pylint-gitlab
+ script:
+ - pylint --exit-zero --output-format=text $(find -type f -name "*.py" ! -path "**/.venv/**" ! -path "**/app/migrations/**") | tee /tmp/pylint.txt
+ - sed -n 's/^Your code has been rated at \([-0-9.]*\)\/.*/\1/p' /tmp/pylint.txt > public/badges/$CI_JOB_NAME.score
+ - pylint --exit-zero --output-format=pylint_gitlab.GitlabCodeClimateReporter $(find -type f -name "*.py" ! -path "**/.venv/**" ! -path "**/app/migrations/**") > codeclimate.json
+ after_script:
+ - anybadge --overwrite --label $CI_JOB_NAME --value=$(cat public/badges/$CI_JOB_NAME.score) --file=public/badges/$CI_JOB_NAME.svg 4=red 6=orange 8=yellow 10=green
+ - |
+ echo "Your score is: $(cat public/badges/$CI_JOB_NAME.score)"
+ artifacts:
+ paths:
+ - public
+ reports:
+ codequality: codeclimate.json
+ when: always
+
docker-build-dev:
image: docker:latest
services:
@@ -132,7 +163,7 @@ 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:
diff --git a/.pylintrc b/.pylintrc
new file mode 100644
index 00000000..d6f4103b
--- /dev/null
+++ b/.pylintrc
@@ -0,0 +1,603 @@
+[MASTER]
+
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code.
+extension-pkg-allow-list=
+
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
+# for backward compatibility.)
+extension-pkg-whitelist=
+
+# Return non-zero exit code if any of these messages/categories are detected,
+# even if score is above --fail-under value. Syntax same as enable. Messages
+# specified are enabled, while categories only check already-enabled messages.
+fail-on=
+
+# Specify a score threshold to be exceeded before program exits with error.
+fail-under=10.0
+
+# Files or directories to be skipped. They should be base names, not paths.
+ignore=
+
+# Add files or directories matching the regex patterns to the ignore-list. The
+# regex matches against paths and can be in Posix or Windows format.
+ignore-paths=app/migrations
+
+# Files or directories matching the regex patterns are skipped. The regex
+# matches against base names, not paths.
+ignore-patterns=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
+# number of processors available to use.
+jobs=0
+
+# Control the amount of potential inferred values when inferring a single
+# object. This can help the performance when dealing with large functions or
+# complex, nested conditions.
+limit-inference-results=100
+
+# List of plugins (as comma separated values of python module names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Minimum Python version to use for version dependent checks. Will default to
+# the version used to run pylint.
+py-version=3.9
+
+# When enabled, pylint would attempt to guess common misconfiguration and emit
+# user-friendly hints instead of false-positive error messages.
+suggestion-mode=yes
+
+# Allow loading of arbitrary C extensions. Extensions are imported into the
+# active Python interpreter and may run arbitrary code.
+unsafe-load-any-extension=no
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
+confidence=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once). You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --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,
+ attribute-defined-outside-init,
+ bad-inline-option,
+ bare-except,
+ broad-except,
+ cell-var-from-loop,
+ consider-iterating-dictionary,
+ consider-using-with,
+ deprecated-pragma,
+ duplicate-code,
+ file-ignored,
+ fixme,
+ import-error,
+ inconsistent-return-statements,
+ invalid-name,
+ locally-disabled,
+ logging-format-interpolation,
+ logging-fstring-interpolation,
+ logging-not-lazy,
+ missing-docstring,
+ no-else-break,
+ no-else-continue,
+ no-else-return,
+ no-self-use,
+ no-value-for-parameter,
+ not-an-iterable,
+ protected-access,
+ simplifiable-condition,
+ simplifiable-if-statement,
+ suppressed-message,
+ too-few-public-methods,
+ too-many-arguments,
+ too-many-branches,
+ too-many-instance-attributes,
+ too-many-locals,
+ too-many-nested-blocks,
+ too-many-public-methods,
+ too-many-return-statements,
+ too-many-statements,
+ use-symbolic-message-instead,
+ useless-suppression,
+ raw-checker-failed
+
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time (only on the command line, not in the configuration file where
+# it should appear only once). See also the "--disable" option for examples.
+enable=c-extension-no-member
+
+
+[REPORTS]
+
+# Python expression which should return a score less than or equal to 10. You
+# have access to the variables 'error', 'warning', 'refactor', and 'convention'
+# which contain the number of messages in each category, as well as 'statement'
+# which is the total number of statements analyzed. This score is used by the
+# global evaluation report (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details.
+#msg-template=
+
+# Set the output format. Available formats are text, parseable, colorized, json
+# and msvs (visual studio). You can also give a reporter class, e.g.
+# mypackage.mymodule.MyReporterClass.
+output-format=text
+
+# Tells whether to display a full report or only the messages.
+reports=no
+
+# Activate the evaluation score.
+score=yes
+
+
+[REFACTORING]
+
+# Maximum number of nested blocks for function / method body
+max-nested-blocks=5
+
+# Complete name of functions that never returns. When checking for
+# inconsistent-return-statements if a never returning function is called then
+# it will be considered as an explicit return statement and no message will be
+# printed.
+never-returning-functions=sys.exit,argparse.parse_error
+
+
+[BASIC]
+
+# Naming style matching correct argument names.
+argument-naming-style=snake_case
+
+# Regular expression matching correct argument names. Overrides argument-
+# naming-style.
+#argument-rgx=
+
+# Naming style matching correct attribute names.
+attr-naming-style=snake_case
+
+# Regular expression matching correct attribute names. Overrides attr-naming-
+# style.
+#attr-rgx=
+
+# Bad variable names which should always be refused, separated by a comma.
+bad-names=foo,
+ bar,
+ baz,
+ toto,
+ tutu,
+ tata
+
+# Bad variable names regexes, separated by a comma. If names match any regex,
+# they will always be refused
+bad-names-rgxs=
+
+# Naming style matching correct class attribute names.
+class-attribute-naming-style=any
+
+# Regular expression matching correct class attribute names. Overrides class-
+# attribute-naming-style.
+#class-attribute-rgx=
+
+# Naming style matching correct class constant names.
+class-const-naming-style=UPPER_CASE
+
+# Regular expression matching correct class constant names. Overrides class-
+# const-naming-style.
+#class-const-rgx=
+
+# Naming style matching correct class names.
+class-naming-style=PascalCase
+
+# Regular expression matching correct class names. Overrides class-naming-
+# style.
+#class-rgx=
+
+# Naming style matching correct constant names.
+const-naming-style=UPPER_CASE
+
+# Regular expression matching correct constant names. Overrides const-naming-
+# style.
+#const-rgx=
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=-1
+
+# Naming style matching correct function names.
+function-naming-style=snake_case
+
+# Regular expression matching correct function names. Overrides function-
+# naming-style.
+#function-rgx=
+
+# Good variable names which should always be accepted, separated by a comma.
+good-names=i,
+ j,
+ k,
+ ex,
+ Run,
+ _
+
+# Good variable names regexes, separated by a comma. If names match any regex,
+# they will always be accepted
+good-names-rgxs=
+
+# Include a hint for the correct naming format with invalid-name.
+include-naming-hint=no
+
+# Naming style matching correct inline iteration names.
+inlinevar-naming-style=any
+
+# Regular expression matching correct inline iteration names. Overrides
+# inlinevar-naming-style.
+#inlinevar-rgx=
+
+# Naming style matching correct method names.
+method-naming-style=snake_case
+
+# Regular expression matching correct method names. Overrides method-naming-
+# style.
+#method-rgx=
+
+# Naming style matching correct module names.
+module-naming-style=snake_case
+
+# Regular expression matching correct module names. Overrides module-naming-
+# style.
+#module-rgx=
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=^_
+
+# List of decorators that produce properties, such as abc.abstractproperty. Add
+# to this list to register other decorators that produce valid properties.
+# These decorators are taken in consideration only for invalid-name.
+property-classes=abc.abstractproperty
+
+# Naming style matching correct variable names.
+variable-naming-style=snake_case
+
+# Regular expression matching correct variable names. Overrides variable-
+# naming-style.
+#variable-rgx=
+
+
+[FORMAT]
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )??$
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
+
+# Maximum number of characters on a single line.
+max-line-length=150
+
+# Maximum number of lines in a module.
+max-module-lines=2000
+
+# Allow the body of a class to be on the same line as the declaration if body
+# contains single statement.
+single-line-class-stmt=no
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+
+[LOGGING]
+
+# The type of string formatting that logging methods do. `old` means using %
+# formatting, `new` is for `{}` formatting.
+logging-format-style=old
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format.
+logging-modules=logging
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,
+ XXX,
+ TODO
+
+# Regular expression of note tags to take in consideration.
+#notes-rgx=
+
+
+[SIMILARITIES]
+
+# Comments are removed from the similarity computation
+ignore-comments=yes
+
+# Docstrings are removed from the similarity computation
+ignore-docstrings=yes
+
+# Imports are removed from the similarity computation
+ignore-imports=no
+
+# Signatures are removed from the similarity computation
+ignore-signatures=no
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+
+[SPELLING]
+
+# Limits count of emitted suggestions for spelling mistakes.
+max-spelling-suggestions=4
+
+# Spelling dictionary name. Available dictionaries: none. To make it work,
+# install the 'python-enchant' package.
+spelling-dict=
+
+# List of comma separated words that should be considered directives if they
+# appear and the beginning of a comment and should not be checked.
+spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains the private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to the private dictionary (see the
+# --spelling-private-dict-file option) instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[STRING]
+
+# This flag controls whether inconsistent-quotes generates a warning when the
+# character used as a quote delimiter is used inconsistently within a module.
+check-quote-consistency=no
+
+# This flag controls whether the implicit-str-concat should generate a warning
+# on implicit string concatenation in sequences defined over several lines.
+check-str-concat-over-line-jumps=no
+
+
+[TYPECHECK]
+
+# List of decorators that produce context managers, such as
+# contextlib.contextmanager. Add to this list to register other decorators that
+# produce valid context managers.
+contextmanager-decorators=contextlib.contextmanager
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=os.*
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# class is considered mixin if its name matches the mixin-class-rgx option.
+ignore-mixin-members=yes
+
+# Tells whether to warn about missing members when the owner of the attribute
+# is inferred to be None.
+ignore-none=yes
+
+# This flag controls whether pylint should warn about no-member and similar
+# checks whenever an opaque object is returned when inferring. The inference
+# can return multiple potential results while evaluating a Python object, but
+# some branches might not be evaluated, which results in partial inference. In
+# that case, it might be useful to still emit no-member and other checks for
+# the rest of the inferred objects.
+ignore-on-opaque-inference=yes
+
+# List of class names for which member attributes should not be checked (useful
+# for classes with dynamically set attributes). This supports the use of
+# qualified names.
+ignored-classes=optparse.Values,thread._local,_thread._local
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis). It
+# supports qualified module names, as well as Unix pattern matching.
+ignored-modules=
+
+# Show a hint with possible names when a member name was not found. The aspect
+# of finding the hint is based on edit distance.
+missing-member-hint=yes
+
+# The minimum edit distance a name should have in order to be considered a
+# similar match for a missing member name.
+missing-member-hint-distance=1
+
+# The total number of similar names that should be taken in consideration when
+# showing a hint for a missing member.
+missing-member-max-choices=1
+
+# Regex pattern to define which classes are considered mixins ignore-mixin-
+# members is set to 'yes'
+mixin-class-rgx=.*[Mm]ixin
+
+# List of decorators that change the signature of a decorated function.
+signature-mutators=
+
+
+[VARIABLES]
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid defining new builtins when possible.
+additional-builtins=
+
+# Tells whether unused global variables should be treated as a violation.
+allow-global-unused-variables=yes
+
+# List of names allowed to shadow builtins
+allowed-redefined-builtins=
+
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,
+ _cb
+
+# A regular expression matching the name of dummy variables (i.e. expected to
+# not be used).
+dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore.
+ignored-argument-names=_.*|^ignored_|^unused_
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# List of qualified module names which can have objects that can redefine
+# builtins.
+redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
+
+
+[CLASSES]
+
+# Warn about protected attribute access inside special methods
+check-protected-access-in-special-methods=no
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,
+ __new__,
+ setUp,
+ __post_init__
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,
+ _fields,
+ _replace,
+ _source,
+ _make
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=cls
+
+
+[DESIGN]
+
+# List of regular expressions of class ancestor names to ignore when counting
+# public methods (see R0903)
+exclude-too-few-public-methods=
+
+# List of qualified class names to ignore when counting class parents (see
+# R0901)
+ignored-parents=
+
+# Maximum number of arguments for function / method.
+max-args=8
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Maximum number of boolean expressions in an if statement (see R0916).
+max-bool-expr=5
+
+# Maximum number of branch for function / method body.
+max-branches=12
+
+# Maximum number of locals for function / method body.
+max-locals=15
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+# Maximum number of return / yield for function / method body.
+max-returns=6
+
+# Maximum number of statements in function / method body.
+max-statements=50
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+
+[IMPORTS]
+
+# List of modules that can be imported at any level, not just the top level
+# one.
+allow-any-import-level=
+
+# Allow wildcard imports from modules that define __all__.
+allow-wildcard-with-all=no
+
+# Analyse import fallback blocks. This can be used to support both Python 2 and
+# 3 compatible code, which means that the block might have code that exists
+# only in one or another interpreter, leading to false positives when analysed.
+analyse-fallback-blocks=no
+
+# Deprecated modules which should not be used, separated by a comma.
+deprecated-modules=
+
+# Output a graph (.gv or any supported image format) of external dependencies
+# to the given file (report RP0402 must not be disabled).
+ext-import-graph=
+
+# Output a graph (.gv or any supported image format) of all (i.e. internal and
+# external) dependencies to the given file (report RP0402 must not be
+# disabled).
+import-graph=
+
+# Output a graph (.gv or any supported image format) of internal dependencies
+# to the given file (report RP0402 must not be disabled).
+int-import-graph=
+
+# Force import order to recognize a module as part of the standard
+# compatibility libraries.
+known-standard-library=
+
+# Force import order to recognize a module as part of a third party library.
+known-third-party=enchant
+
+# Couples of modules and preferred modules, separated by a comma.
+preferred-modules=
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "BaseException, Exception".
+overgeneral-exceptions=BaseException,
+ Exception
diff --git a/app/classes/controllers/crafty_perms_controller.py b/app/classes/controllers/crafty_perms_controller.py
index b5f7f4e5..d73774c7 100644
--- a/app/classes/controllers/crafty_perms_controller.py
+++ b/app/classes/controllers/crafty_perms_controller.py
@@ -1,23 +1,6 @@
-import os
-import time
import logging
-import sys
-import yaml
-import asyncio
-import shutil
-import tempfile
-import zipfile
-from distutils import dir_util
-
-from app.classes.shared.helpers import helper
-from app.classes.shared.console import console
from app.classes.models.crafty_permissions import crafty_permissions, Enum_Permissions_Crafty
-
-from app.classes.shared.server import Server
-from app.classes.minecraft.server_props import ServerProps
-from app.classes.minecraft.serverjars import server_jar_obj
-from app.classes.minecraft.stats import Stats
from app.classes.models.users import ApiKeys
logger = logging.getLogger(__name__)
@@ -43,15 +26,15 @@ class Crafty_Perms_Controller:
return crafty_permissions.can_add_in_crafty(user_id, Enum_Permissions_Crafty.Server_Creation)
@staticmethod
- def can_add_user(user_id):
- #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(user_id):
- #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 4e8dacab..198588b5 100644
--- a/app/classes/controllers/management_controller.py
+++ b/app/classes/controllers/management_controller.py
@@ -1,25 +1,8 @@
-import os
-import time
import logging
-import sys
-import yaml
-import asyncio
-import shutil
-import tempfile
-import zipfile
-from distutils import dir_util
-
-from app.classes.shared.helpers import helper
-from app.classes.shared.console import console
from app.classes.models.management import management_helper
from app.classes.models.servers import servers_helper
-from app.classes.shared.server import Server
-from app.classes.minecraft.server_props import ServerProps
-from app.classes.minecraft.serverjars import server_jar_obj
-from app.classes.minecraft.stats import Stats
-
logger = logging.getLogger(__name__)
class Management_Controller:
@@ -43,14 +26,13 @@ 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, "issued command {} for server {}".format(command, 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
#************************************************************************************************
@@ -71,7 +53,16 @@ class Management_Controller:
#************************************************************************************************
@staticmethod
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)
+ return management_helper.create_scheduled_task(
+ server_id,
+ action,
+ interval,
+ interval_type,
+ start_time,
+ command,
+ comment,
+ enabled
+ )
@staticmethod
def delete_scheduled_task(schedule_id):
diff --git a/app/classes/controllers/roles_controller.py b/app/classes/controllers/roles_controller.py
index aa1e936d..4071ba7e 100644
--- a/app/classes/controllers/roles_controller.py
+++ b/app/classes/controllers/roles_controller.py
@@ -1,25 +1,10 @@
-import os
-import time
import logging
-import sys
-import yaml
-import asyncio
-import shutil
-import tempfile
-import zipfile
-from distutils import dir_util
-
-from app.classes.shared.helpers import helper
-from app.classes.shared.console import console
from app.classes.models.roles import roles_helper
from app.classes.models.server_permissions import server_permissions
from app.classes.models.users import users_helper
-from app.classes.shared.server import Server
-from app.classes.minecraft.server_props import ServerProps
-from app.classes.minecraft.serverjars import server_jar_obj
-from app.classes.minecraft.stats import Stats
+from app.classes.shared.helpers import helper
logger = logging.getLogger(__name__)
@@ -45,7 +30,6 @@ class Roles_Controller:
base_data = Roles_Controller.get_role_with_servers(role_id)
up_data = {}
added_servers = set()
- edited_servers = set()
removed_servers = set()
for key in role_data:
if key == "role_id":
@@ -56,7 +40,7 @@ class Roles_Controller:
elif base_data[key] != role_data[key]:
up_data[key] = role_data[key]
up_data['last_update'] = helper.get_time_as_string()
- logger.debug("role: {} +server:{} -server{}".format(role_data, added_servers, removed_servers))
+ 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']:
@@ -96,4 +80,4 @@ class Roles_Controller:
return role
else:
#logger.debug("role: ({}) {}".format(role_id, {}))
- return {}
\ No newline at end of file
+ return {}
diff --git a/app/classes/controllers/server_perms_controller.py b/app/classes/controllers/server_perms_controller.py
index 9f49f324..9afa5bd2 100644
--- a/app/classes/controllers/server_perms_controller.py
+++ b/app/classes/controllers/server_perms_controller.py
@@ -1,32 +1,20 @@
-import os
-import time
import logging
-import sys
-import yaml
-import asyncio
-import shutil
-import tempfile
-import zipfile
-from distutils import dir_util
-from app.classes.shared.helpers import helper
-from app.classes.shared.console import console
-
-from app.classes.shared.main_models import db_helper
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
-from app.classes.shared.server import Server
-from app.classes.minecraft.server_props import ServerProps
-from app.classes.minecraft.serverjars import server_jar_obj
-from app.classes.minecraft.stats import Stats
+from app.classes.shared.main_models import db_helper
logger = logging.getLogger(__name__)
class Server_Perms_Controller:
+ @staticmethod
+ def get_server_user_list(server_id):
+ return server_permissions.get_server_user_list(server_id)
+
@staticmethod
def list_defined_permissions():
permissions_list = server_permissions.get_permissions_list()
@@ -54,7 +42,9 @@ class Server_Perms_Controller:
def backup_role_swap(old_server_id, new_server_id):
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,
+ 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')
#************************************************************************************************
@@ -85,19 +75,6 @@ class Server_Perms_Controller:
def get_api_key_permissions_list(key: ApiKeys, server_id: str):
return server_permissions.get_api_key_permissions_list(key, server_id)
- @staticmethod
- def get_user_id_permissions_list(user_id: str, server_id: str):
- return server_permissions.get_user_id_permissions_list(user_id, server_id)
-
- @staticmethod
- def get_api_key_id_permissions_list(key_id: str, server_id: str):
- key = users_helper.get_user_api_key(key_id)
- return server_permissions.get_api_key_permissions_list(key, server_id)
-
- @staticmethod
- def get_api_key_permissions_list(key: ApiKeys, server_id: str):
- return server_permissions.get_api_key_permissions_list(key, server_id)
-
@staticmethod
def get_authorized_servers_stats_from_roles(user_id):
user_roles = users_helper.get_user_roles_id(user_id)
diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py
index c2ed77a9..24b19cf7 100644
--- a/app/classes/controllers/servers_controller.py
+++ b/app/classes/controllers/servers_controller.py
@@ -1,29 +1,15 @@
-from app.classes.controllers.roles_controller import Roles_Controller
import os
-import time
import logging
import json
-import sys
-import yaml
-import asyncio
-import shutil
-import tempfile
-import zipfile
-from distutils import dir_util
-from app.classes.shared.helpers import helper
-from app.classes.shared.console import console
-
-from app.classes.shared.main_models import db_helper
from app.classes.models.servers import servers_helper
-from app.classes.models.roles import roles_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.shared.server import Server
-from app.classes.minecraft.server_props import ServerProps
-from app.classes.minecraft.serverjars import server_jar_obj
-from app.classes.minecraft.stats import Stats
+from app.classes.shared.helpers import helper
+from app.classes.shared.main_models import db_helper
+
+from app.classes.controllers.roles_controller import Roles_Controller
logger = logging.getLogger(__name__)
@@ -33,8 +19,26 @@ class Servers_Controller:
# Generic Servers Methods
#************************************************************************************************
@staticmethod
- def create_server(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=25565):
- return servers_helper.create_server(name, server_uuid, server_dir, backup_path, server_command, server_file, server_log_file, server_stop, server_port)
+ def create_server(
+ 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=25565):
+ return servers_helper.create_server(
+ name,
+ server_uuid,
+ server_dir,
+ backup_path,
+ server_command,
+ server_file,
+ server_log_file,
+ server_stop,
+ server_port)
@staticmethod
def get_server_obj(server_id):
@@ -183,7 +187,7 @@ class Servers_Controller:
path = os.path.join(server_path, 'banned-players.json')
try:
- with open(helper.get_os_understandable_path(path)) as file:
+ with open(helper.get_os_understandable_path(path), encoding='utf-8') as file:
content = file.read()
file.close()
except Exception as ex:
@@ -210,4 +214,3 @@ class Servers_Controller:
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 399da361..12bb4c2c 100644
--- a/app/classes/controllers/users_controller.py
+++ b/app/classes/controllers/users_controller.py
@@ -1,28 +1,16 @@
-import os
-import time
import logging
-import sys
from typing import Optional
-import yaml
-import asyncio
-import shutil
-import tempfile
-import zipfile
-from distutils import dir_util
-
from app.classes.shared.helpers import helper
-from app.classes.shared.console import console
-
-from app.classes.models.users import Users, users_helper
from app.classes.shared.authentication import authentication
+
+from app.classes.models.users import users_helper
from app.classes.models.crafty_permissions import crafty_permissions, Enum_Permissions_Crafty
-from app.classes.models.management import management_helper
logger = logging.getLogger(__name__)
class Users_Controller:
-
+
#************************************************************************************************
# Users Methods
#************************************************************************************************
@@ -60,7 +48,6 @@ class Users_Controller:
up_data = {}
added_roles = set()
removed_roles = set()
- removed_servers = set()
for key in user_data:
if key == "user_id":
continue
@@ -74,7 +61,7 @@ class Users_Controller:
up_data[key] = user_data[key]
up_data['last_update'] = helper.get_time_as_string()
up_data['lang'] = user_data['lang']
- logger.debug("user: {} +role:{} -role:{}".format(user_data, added_roles, removed_roles))
+ 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')
@@ -90,9 +77,14 @@ class Users_Controller:
limit_user_creation = 0
limit_role_creation = 0
- crafty_permissions.add_or_update_user(user_id, permissions_mask, limit_server_creation, limit_user_creation, limit_role_creation)
+ crafty_permissions.add_or_update_user(
+ user_id,
+ permissions_mask,
+ limit_server_creation,
+ limit_user_creation,
+ limit_role_creation)
- users_helper.delete_user_roles(user_id, removed_roles)
+ users_helper.delete_user_roles(user_id, removed_roles)
users_helper.update_user(user_id, up_data)
@@ -121,7 +113,7 @@ class Users_Controller:
# ************************************************************************************************
# User Roles Methods
# ************************************************************************************************
-
+
@staticmethod
def get_user_roles_id(user_id):
return users_helper.get_user_roles_id(user_id)
@@ -137,7 +129,7 @@ class Users_Controller:
@staticmethod
def add_user_roles(user):
return users_helper.add_user_roles(user)
-
+
@staticmethod
def user_role_query(user_id):
return users_helper.user_role_query(user_id)
diff --git a/app/classes/minecraft/mc_ping.py b/app/classes/minecraft/mc_ping.py
index 0d21d9d4..52d3cdec 100644
--- a/app/classes/minecraft/mc_ping.py
+++ b/app/classes/minecraft/mc_ping.py
@@ -1,11 +1,10 @@
-from app.classes.shared.helpers import Helpers
import struct
import socket
import base64
import json
-import sys
import os
import logging.config
+
from app.classes.shared.console import console
logger = logging.getLogger(__name__)
@@ -41,7 +40,7 @@ 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']))
+ lines.append(get_code_format(e['color']))
#Then append the text
if "text" in e.keys():
if e['text'] == '\n':
@@ -101,13 +100,13 @@ def get_code_format(format_name):
if format_name in data.keys():
return data.get(format_name)
else:
- logger.error("Format MOTD Error: format name {} does not exist".format(format_name))
- console.error("Format MOTD Error: format name {} does not exist".format(format_name))
+ 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")
return ""
except Exception as e:
- logger.critical("Config File Error: Unable to read {} due to {}".format(format_file, e))
- console.critical("Config File Error: Unable to read {} due to {}".format(format_file, e))
+ logger.critical(f"Config File Error: Unable to read {format_file} due to {e}")
+ console.critical(f"Config File Error: Unable to read {format_file} due to {e}")
return ""
@@ -126,15 +125,14 @@ def ping(ip, port):
j += 1
if j > 5:
raise ValueError('var_int too big')
- if not (k & 0x80):
+ if not k & 0x80:
return i
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect((ip, port))
- except socket.error as err:
- pass
+ except socket.error:
return False
try:
@@ -163,7 +161,7 @@ def ping(ip, port):
return False
data += chunk
- logger.debug("Server reports this data on ping: {}".format(data))
+ logger.debug(f"Server reports this data on ping: {data}")
return Server(json.loads(data))
finally:
sock.close()
diff --git a/app/classes/minecraft/server_props.py b/app/classes/minecraft/server_props.py
index 356fac67..027e11ac 100644
--- a/app/classes/minecraft/server_props.py
+++ b/app/classes/minecraft/server_props.py
@@ -9,7 +9,7 @@ class ServerProps:
def _parse(self):
"""Loads and parses the file specified in self.filepath"""
- with open(self.filepath) as fp:
+ with open(self.filepath, encoding='utf-8') as fp:
line = fp.readline()
d = {}
if os.path.exists(".header"):
@@ -24,7 +24,7 @@ class ServerProps:
s2 = s[s.find('=')+1:]
d[s1] = s2
else:
- with open(".header", "a+") 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+") as f:
+ with open(self.filepath, "a+", encoding='utf-8') as f:
f.truncate(0)
- with open(".header") 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 386268c5..95e013aa 100644
--- a/app/classes/minecraft/serverjars.py
+++ b/app/classes/minecraft/serverjars.py
@@ -1,4 +1,3 @@
-import os
import sys
import json
import threading
@@ -9,19 +8,15 @@ from datetime import datetime
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
-from app.classes.models.servers import Servers
-from app.classes.minecraft.server_props import ServerProps
-from app.classes.web.websocket_helper import websocket_helper
-from app.classes.models.server_permissions import server_permissions
logger = logging.getLogger(__name__)
try:
import requests
-except ModuleNotFoundError as e:
- logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
- console.critical("Import Error: Unable to load {} module".format(e.name))
+except ModuleNotFoundError as err:
+ logger.critical(f"Import Error: Unable to load {err.name} module", exc_info=True)
+ console.critical(f"Import Error: Unable to load {err.name} module")
sys.exit(1)
@@ -31,7 +26,7 @@ class ServerJars:
self.base_url = "https://serverjars.com"
def _get_api_result(self, call_url: str):
- full_url = "{base}{call_url}".format(base=self.base_url, call_url=call_url)
+ full_url = f"{self.base_url}{call_url}"
try:
r = requests.get(full_url, timeout=2)
@@ -39,20 +34,20 @@ class ServerJars:
if r.status_code not in [200, 201]:
return {}
except Exception as e:
- logger.error("Unable to connect to serverjar.com api due to error: {}".format(e))
+ logger.error(f"Unable to connect to serverjar.com api due to error: {e}")
return {}
try:
api_data = json.loads(r.content)
except Exception as e:
- logger.error("Unable to parse serverjar.com api result due to error: {}".format(e))
+ 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', {})
if api_result != "success":
- logger.error("Api returned a failed status: {}".format(api_result))
+ logger.error(f"Api returned a failed status: {api_result}")
return {}
return api_response
@@ -62,11 +57,11 @@ class ServerJars:
cache_file = helper.serverjar_cache
cache = {}
try:
- with open(cache_file, "r") as f:
+ with open(cache_file, "r", encoding='utf-8') as f:
cache = json.load(f)
except Exception as e:
- logger.error("Unable to read serverjars.com cache file: {}".format(e))
+ logger.error(f"Unable to read serverjars.com cache file: {e}")
return cache
@@ -100,7 +95,7 @@ class ServerJars:
def _check_api_alive(self):
logger.info("Checking serverjars.com API status")
- check_url = "{base}/api/fetchTypes".format(base=self.base_url)
+ check_url = f"{self.base_url}/api/fetchTypes"
try:
r = requests.get(check_url, timeout=2)
@@ -108,7 +103,7 @@ class ServerJars:
logger.info("Serverjars.com API is alive")
return True
except Exception as e:
- logger.error("Unable to connect to serverjar.com api due to error: {}".format(e))
+ logger.error(f"Unable to connect to serverjar.com api due to error: {e}")
return {}
logger.error("unable to contact serverjars.com api")
@@ -154,15 +149,15 @@ class ServerJars:
# save our cache
try:
- with open(cache_file, "w") 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("Unable to update serverjars.com cache file: {}".format(e))
+ logger.error(f"Unable to update serverjars.com cache file: {e}")
def _get_jar_details(self, jar_type='servers'):
- url = '/api/fetchAll/{type}'.format(type=jar_type)
+ url = f'/api/fetchAll/{jar_type}'
response = self._get_api_result(url)
temp = []
for v in response:
@@ -175,12 +170,12 @@ class ServerJars:
response = self._get_api_result(url)
return response
- def download_jar(self, server, version, path, name):
- update_thread = threading.Thread(target=self.a_download_jar, daemon=True, name="exe_download", args=(server, version, path, name))
+ def download_jar(self, server, version, path):
+ update_thread = threading.Thread(target=self.a_download_jar, daemon=True, args=(server, version, path))
update_thread.start()
- def a_download_jar(self, server, version, path, name):
- fetch_url = "{base}/api/fetchJar/{server}/{version}".format(base=self.base_url, server=server, version=version)
+ def a_download_jar(self, server, version, path):
+ fetch_url = f"{self.base_url}/api/fetchJar/{server}/{version}"
# open a file stream
with requests.get(fetch_url, timeout=2, stream=True) as r:
@@ -189,9 +184,7 @@ class ServerJars:
shutil.copyfileobj(r.raw, output)
except Exception as e:
- logger.error("Unable to save jar to {path} due to error:{error}".format(path=path, error=e))
- pass
-
+ logger.error(f"Unable to save jar to {path} due to error:{e}")
return False
diff --git a/app/classes/minecraft/stats.py b/app/classes/minecraft/stats.py
index ee632cf6..458c9532 100644
--- a/app/classes/minecraft/stats.py
+++ b/app/classes/minecraft/stats.py
@@ -1,16 +1,15 @@
import os
import json
-import time
-import psutil
import logging
import datetime
import base64
+import psutil
+from app.classes.models.management import Host_Stats
+from app.classes.models.servers import Server_Stats, servers_helper
from app.classes.shared.helpers import helper
from app.classes.minecraft.mc_ping import ping
-from app.classes.models.management import Host_Stats
-from app.classes.models.servers import Server_Stats, servers_helper
logger = logging.getLogger(__name__)
@@ -64,8 +63,6 @@ class Stats:
real_cpu = round(p.cpu_percent(interval=0.5) / psutil.cpu_count(), 2)
- process_start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(p.create_time()))
-
# this is a faster way of getting data for a process
with p.oneshot():
process_stats = {
@@ -76,7 +73,7 @@ class Stats:
return process_stats
except Exception as e:
- logger.error("Unable to get process details for pid: {} due to error: {}".format(process_pid, e))
+ logger.error(f"Unable to get process details for pid: {process_pid} due to error: {e}")
# Dummy Data
process_stats = {
@@ -119,13 +116,13 @@ class Stats:
total_size = 0
# do a scan of the directories in the server path.
- for root, dirs, files in os.walk(world_path, topdown=False):
+ for root, dirs, _files in os.walk(world_path, topdown=False):
# for each directory we find
for name in dirs:
# if the directory name is "region"
- if name == "region":
+ if str(name) == "region":
# log it!
logger.debug("Path %s is called region. Getting directory size", os.path.join(root, name))
@@ -144,14 +141,14 @@ class Stats:
online_stats = json.loads(ping_obj.players)
except Exception as e:
- logger.info("Unable to read json from ping_obj: {}".format(e))
- pass
+ logger.info(f"Unable to read json from ping_obj: {e}")
+
try:
server_icon = base64.encodebytes(ping_obj.icon)
except Exception as e:
server_icon = False
- logger.info("Unable to read the server icon : {}".format(e))
+ logger.info(f"Unable to read the server icon : {e}")
ping_data = {
'online': online_stats.get("online", 0),
@@ -168,18 +165,18 @@ class Stats:
server = servers_helper.get_server_data_by_id(server_id)
- logger.info("Getting players for server {}".format(server))
+ logger.info(f"Getting players for server {server}")
# get our settings and data dictionaries
- server_settings = server.get('server_settings', {})
- server_data = server.get('server_data_obj', {})
+ # 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']
- logger.debug("Pinging {} on port {}".format(internal_ip, server_port))
+ logger.debug("Pinging {internal_ip} on port {server_port}")
int_mc_ping = ping(internal_ip, int(server_port))
ping_data = {}
@@ -205,7 +202,7 @@ class Stats:
server = servers_helper.get_server_data_by_id(server_id)
- logger.debug('Getting stats for server: {}'.format(server_id))
+ logger.debug(f'Getting stats for server: {server_id}')
# get our server object, settings and data dictionaries
server_obj = s.get('server_obj', None)
@@ -223,8 +220,9 @@ class Stats:
# TODO: search server properties file for possible override of 127.0.0.1
internal_ip = server['server_ip']
server_port = server['server_port']
+ server = s.get('server_name', f"ID#{server_id}")
- logger.debug("Pinging server '{}' on {}:{}".format(s.get('server_name', "ID#{}".format(server_id)), internal_ip, server_port))
+ logger.debug("Pinging server '{server}' on {internal_ip}:{server_port}")
int_mc_ping = ping(internal_ip, int(server_port))
int_data = False
@@ -263,7 +261,7 @@ class Stats:
server_stats = {}
server = self.controller.get_server_obj(server_id)
- logger.debug('Getting stats for server: {}'.format(server_id))
+ logger.debug(f'Getting stats for server: {server_id}')
# get our server object, settings and data dictionaries
server_obj = self.controller.get_server_obj(server_id)
@@ -285,7 +283,7 @@ class Stats:
server_port = server_settings.get('server-port', "25565")
- logger.debug("Pinging server '{}' on {}:{}".format(server.name, internal_ip, server_port))
+ logger.debug(f"Pinging server '{server.name}' on {internal_ip}:{server_port}")
int_mc_ping = ping(internal_ip, int(server_port))
int_data = False
@@ -360,4 +358,4 @@ 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()
\ No newline at end of file
+ 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 c0bc9c6b..0a3644ed 100644
--- a/app/classes/models/crafty_permissions.py
+++ b/app/classes/models/crafty_permissions.py
@@ -1,26 +1,23 @@
-import os
import sys
import logging
-import datetime
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
-from app.classes.models.users import Users, ApiKeys
from app.classes.shared.permission_helper import permission_helper
+from app.classes.models.users import Users, ApiKeys
+
logger = logging.getLogger(__name__)
peewee_logger = logging.getLogger('peewee')
peewee_logger.setLevel(logging.INFO)
try:
- from peewee import *
- from playhouse.shortcuts import model_to_dict
+ from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, IntegerField, DoesNotExist
from enum import Enum
- import yaml
except ModuleNotFoundError as e:
- logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
- console.critical("Import Error: Unable to load {} module".format(e.name))
+ logger.critical(f"Import Error: Unable to load {e.name} module", exc_info=True)
+ console.critical(f"Import Error: Unable to load {e.name} module")
sys.exit(1)
database = SqliteDatabase(helper.db_path, pragmas={
@@ -124,7 +121,7 @@ class Permissions_Crafty:
def get_User_Crafty(user_id):
try:
user_crafty = User_Crafty.select().where(User_Crafty.user_id == user_id).get()
- except User_Crafty.DoesNotExist:
+ except DoesNotExist:
user_crafty = User_Crafty.insert({
User_Crafty.user_id: user_id,
User_Crafty.permissions: "000",
@@ -173,7 +170,6 @@ class Permissions_Crafty:
@staticmethod
def get_crafty_limit_value(user_id, permission):
- user_crafty = crafty_permissions.get_User_Crafty(user_id)
quantity_list = crafty_permissions.get_permission_quantity_list(user_id)
return quantity_list[permission]
@@ -206,4 +202,4 @@ class Permissions_Crafty:
-crafty_permissions = Permissions_Crafty()
\ No newline at end of file
+crafty_permissions = Permissions_Crafty()
diff --git a/app/classes/models/management.py b/app/classes/models/management.py
index 19508aa0..bd47f0c4 100644
--- a/app/classes/models/management.py
+++ b/app/classes/models/management.py
@@ -1,4 +1,3 @@
-import os
import sys
import logging
import datetime
@@ -6,26 +5,24 @@ import datetime
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
from app.classes.shared.main_models import db_helper
-from app.classes.models.users import Users, users_helper
-from app.classes.models.servers import Servers, servers_helper
-from app.classes.web.websocket_helper import websocket_helper
-from app.classes.models.server_permissions import server_permissions
-import time
+from app.classes.models.users import Users, users_helper
+from app.classes.models.servers import Servers
+from app.classes.models.server_permissions import server_permissions
+
+from app.classes.web.websocket_helper import websocket_helper
logger = logging.getLogger(__name__)
peewee_logger = logging.getLogger('peewee')
peewee_logger.setLevel(logging.INFO)
try:
- from peewee import *
+ from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, IntegerField, DateTimeField, FloatField, TextField, AutoField, BooleanField
from playhouse.shortcuts import model_to_dict
- from enum import Enum
- import yaml
except ModuleNotFoundError as e:
- logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
- console.critical("Import Error: Unable to load {} module".format(e.name))
+ logger.critical(f"Import Error: Unable to load {e.name} module", exc_info=True)
+ console.critical(f"Import Error: Unable to load {e.name} module")
sys.exit(1)
database = SqliteDatabase(helper.db_path, pragmas={
@@ -140,7 +137,7 @@ class helpers_management:
#************************************************************************************************
@staticmethod
def get_latest_hosts_stats():
- query = Host_Stats.select().order_by(Host_Stats.id.desc()).get()
+ query = Host_Stats.select().order_by(Host_Stats).get()
return model_to_dict(query)
#************************************************************************************************
@@ -163,7 +160,7 @@ class helpers_management:
@staticmethod
def mark_command_complete(command_id=None):
if command_id is not None:
- logger.debug("Marking Command {} completed".format(command_id))
+ logger.debug(f"Marking Command {command_id} completed")
Commands.update({
Commands.executed: True
}).where(Commands.command_id == command_id).execute()
@@ -178,10 +175,10 @@ class helpers_management:
@staticmethod
def add_to_audit_log(user_id, log_msg, server_id=None, source_ip=None):
- logger.debug("Adding to audit log User:{} - Message: {} ".format(user_id, log_msg))
+ logger.debug(f"Adding to audit log User:{user_id} - Message: {log_msg} ")
user_data = users_helper.get_user(user_id)
- audit_msg = "{} {}".format(str(user_data['username']).capitalize(), log_msg)
+ audit_msg = f"{str(user_data['username']).capitalize()} {log_msg}"
server_users = server_permissions.get_server_user_list(server_id)
for user in server_users:
@@ -209,7 +206,17 @@ class helpers_management:
# Schedules Methods
#************************************************************************************************
@staticmethod
- def create_scheduled_task(server_id, action, interval, interval_type, start_time, command, comment=None, enabled=True, one_time=False, cron_string='* * * * *'):
+ def create_scheduled_task(
+ server_id,
+ action,
+ interval,
+ interval_type,
+ start_time,
+ command,
+ comment=None,
+ enabled=True,
+ one_time=False,
+ cron_string='* * * * *'):
sch_id = Schedules.insert({
Schedules.server_id: server_id,
Schedules.action: action,
@@ -236,11 +243,11 @@ class helpers_management:
@staticmethod
def delete_scheduled_task_by_server(server_id):
- Schedules.delete().where(Schedules.server_id == int(server_id)).execute()
+ Schedules.delete().where(Schedules.server_id == int(server_id)).execute()
@staticmethod
def get_scheduled_task(schedule_id):
- return model_to_dict(Schedules.get(Schedules.schedule_id == schedule_id)).execute()
+ return model_to_dict(Schedules.get(Schedules.schedule_id == schedule_id))
@staticmethod
def get_scheduled_task_model(schedule_id):
@@ -256,7 +263,7 @@ class helpers_management:
@staticmethod
def get_schedules_enabled():
- return Schedules.select().where(Schedules.enabled == True).execute()
+ return Schedules.select().where(Schedules.enabled is True).execute()
#************************************************************************************************
# Backups Methods
@@ -282,12 +289,10 @@ class helpers_management:
@staticmethod
def set_backup_config(server_id: int, backup_path: str = None, max_backups: int = None):
- logger.debug("Updating server {} backup config with {}".format(server_id, locals()))
+ logger.debug(f"Updating server {server_id} backup config with {locals()}")
try:
- row = Backups.select().where(Backups.server_id == server_id).join(Servers)[0]
new_row = False
conf = {}
- schd = {}
except IndexError:
conf = {
"directories": None,
@@ -304,13 +309,13 @@ class helpers_management:
else:
u1 = 0
u2 = Backups.update(conf).where(Backups.server_id == server_id).execute()
- logger.debug("Updating existing backup record. {}+{} rows affected".format(u1, u2))
+ 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:
- u = Servers.update(backup_path=backup_path).where(Servers.server_id == server_id)
- b = Backups.create(**conf)
+ Servers.update(backup_path=backup_path).where(Servers.server_id == server_id)
+ Backups.create(**conf)
logger.debug("Creating new backup record.")
diff --git a/app/classes/models/roles.py b/app/classes/models/roles.py
index 49146b93..ddecd798 100644
--- a/app/classes/models/roles.py
+++ b/app/classes/models/roles.py
@@ -1,4 +1,3 @@
-import os
import sys
import logging
import datetime
@@ -11,14 +10,12 @@ peewee_logger = logging.getLogger('peewee')
peewee_logger.setLevel(logging.INFO)
try:
- from peewee import *
+ from peewee import SqliteDatabase, Model, CharField, DoesNotExist, AutoField, DateTimeField
from playhouse.shortcuts import model_to_dict
- from enum import Enum
- import yaml
except ModuleNotFoundError as e:
- logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
- console.critical("Import Error: Unable to load {} module".format(e.name))
+ logger.critical(f"Import Error: Unable to load {e.name} module", exc_info=True)
+ console.critical(f"Import Error: Unable to load {e.name} module")
sys.exit(1)
database = SqliteDatabase(helper.db_path, pragmas={
@@ -45,7 +42,7 @@ class helper_roles:
@staticmethod
def get_all_roles():
query = Roles.select()
- return query
+ return query
@staticmethod
def get_roleid_by_name(role_name):
@@ -81,5 +78,5 @@ class helper_roles:
if not roles_helper.get_role(role_id):
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 5d150187..b666deba 100644
--- a/app/classes/models/server_permissions.py
+++ b/app/classes/models/server_permissions.py
@@ -1,28 +1,26 @@
-import os
import sys
import logging
-import datetime
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
+from app.classes.shared.permission_helper import permission_helper
+
from app.classes.models.servers import Servers
from app.classes.models.roles import Roles
from app.classes.models.users import User_Roles, users_helper, ApiKeys, Users
-from app.classes.shared.permission_helper import permission_helper
+
logger = logging.getLogger(__name__)
peewee_logger = logging.getLogger('peewee')
peewee_logger.setLevel(logging.INFO)
try:
- from peewee import *
- from playhouse.shortcuts import model_to_dict
+ from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, CompositeKey, JOIN
from enum import Enum
- import yaml
except ModuleNotFoundError as e:
- logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
- console.critical("Import Error: Unable to load {} module".format(e.name))
+ logger.critical(f"Import Error: Unable to load {e.name} module", exc_info=True)
+ console.critical(f"Import Error: Unable to load {e.name} module")
sys.exit(1)
database = SqliteDatabase(helper.db_path, pragmas={
@@ -118,7 +116,8 @@ class Permissions_Servers:
@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
@@ -181,12 +180,13 @@ class Permissions_Servers:
def get_server_user_list(server_id):
final_users = []
server_roles = Role_Servers.select().where(Role_Servers.server_id == server_id)
+ # pylint: disable=singleton-comparison
super_users = Users.select().where(Users.superuser == True)
for role in server_roles:
users = User_Roles.select().where(User_Roles.role_id == role.role_id)
for user in users:
if user.user_id.user_id not in final_users:
- final_users.append(user.user_id.user_id)
+ final_users.append(user.user_id.user_id)
for suser in super_users:
if suser.user_id not in final_users:
final_users.append(suser.user_id)
diff --git a/app/classes/models/servers.py b/app/classes/models/servers.py
index 0302efd1..d29e903e 100644
--- a/app/classes/models/servers.py
+++ b/app/classes/models/servers.py
@@ -1,11 +1,9 @@
-import os
import sys
import logging
import datetime
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
-
from app.classes.shared.main_models import db_helper
logger = logging.getLogger(__name__)
@@ -13,14 +11,11 @@ peewee_logger = logging.getLogger('peewee')
peewee_logger.setLevel(logging.INFO)
try:
- from peewee import *
- from playhouse.shortcuts import model_to_dict
- from enum import Enum
- import yaml
+ from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, AutoField, DateTimeField, BooleanField, IntegerField, FloatField
except ModuleNotFoundError as e:
- logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
- console.critical("Import Error: Unable to load {} module".format(e.name))
+ logger.critical(f"Import Error: Unable to load {e.name} module", exc_info=True)
+ console.critical(f"Import Error: Unable to load {e.name} module")
sys.exit(1)
database = SqliteDatabase(helper.db_path, pragmas={
@@ -94,7 +89,16 @@ class helper_servers:
# Generic Servers Methods
#************************************************************************************************
@staticmethod
- def create_server(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=25565):
+ def create_server(
+ 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=25565):
return Servers.insert({
Servers.server_name: name,
Servers.server_uuid: server_uuid,
@@ -153,7 +157,7 @@ class helper_servers:
@staticmethod
def get_server_friendly_name(server_id):
server_data = servers_helper.get_server_data_by_id(server_id)
- friendly_name = "{} with ID: {}".format(server_data.get('server_name', None), server_data.get('server_id', 0))
+ friendly_name = f"{server_data.get('server_name', None)} with ID: {server_data.get('server_id', 0)}"
return friendly_name
#************************************************************************************************
@@ -177,9 +181,10 @@ class helper_servers:
@staticmethod
def set_update(server_id, value):
try:
- row = Server_Stats.select().where(Server_Stats.server_id == server_id)
+ #Checks if server even exists
+ Server_Stats.select().where(Server_Stats.server_id == server_id)
except Exception as ex:
- logger.error("Database entry not found. ".format(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()
@@ -192,9 +197,11 @@ class helper_servers:
def set_first_run(server_id):
#Sets first run to false
try:
- row = Server_Stats.select().where(Server_Stats.server_id == server_id)
+ #Checks if server even exists
+ Server_Stats.select().where(Server_Stats.server_id == server_id)
except Exception as ex:
- logger.error("Database entry not found. ".format(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()
@@ -206,7 +213,12 @@ class helper_servers:
@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_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
@@ -220,9 +232,10 @@ class helper_servers:
@staticmethod
def set_waiting_start(server_id, value):
try:
- row = Server_Stats.select().where(Server_Stats.server_id == server_id)
+ # Checks if server even exists
+ Server_Stats.select().where(Server_Stats.server_id == server_id)
except Exception as ex:
- logger.error("Database entry not found. ".format(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()
@@ -232,4 +245,4 @@ class helper_servers:
return waiting_start.waiting_start
-servers_helper = helper_servers()
\ No newline at end of file
+servers_helper = helper_servers()
diff --git a/app/classes/models/users.py b/app/classes/models/users.py
index c58d4a4a..e154e885 100644
--- a/app/classes/models/users.py
+++ b/app/classes/models/users.py
@@ -1,8 +1,7 @@
-import os
import sys
import logging
import datetime
-from typing import Optional, List, Union
+from typing import Optional, Union
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
@@ -14,14 +13,12 @@ peewee_logger = logging.getLogger('peewee')
peewee_logger.setLevel(logging.INFO)
try:
- from peewee import *
+ from peewee import SqliteDatabase, Model, ForeignKeyField, CharField, AutoField, DateTimeField, BooleanField, CompositeKey, DoesNotExist, JOIN
from playhouse.shortcuts import model_to_dict
- from enum import Enum
- import yaml
except ModuleNotFoundError as e:
- logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
- console.critical("Import Error: Unable to load {} module".format(e.name))
+ logger.critical(f"Import Error: Unable to load {e.name} module", exc_info=True)
+ console.critical(f"Import Error: Unable to load {e.name} module")
sys.exit(1)
database = SqliteDatabase(helper.db_path, pragmas={
@@ -58,7 +55,7 @@ class ApiKeys(Model):
token_id = AutoField()
name = CharField(default='', unique=True, index=True)
created = DateTimeField(default=datetime.datetime.now)
- user = ForeignKeyField(Users, backref='api_token', index=True)
+ user_id = ForeignKeyField(Users, backref='api_token', index=True)
server_permissions = CharField(default='00000000')
crafty_permissions = CharField(default='000')
superuser = BooleanField(default=False)
@@ -138,10 +135,12 @@ class helper_users:
#logger.debug("user: ({}) {}".format(user_id, {}))
return {}
+ @staticmethod
def check_system_user(user_id):
try:
- Users.get(Users.user_id == user_id).user_id == user_id
- return True
+ result = Users.get(Users.user_id == user_id).user_id == user_id
+ if result:
+ return True
except:
return False
@@ -177,6 +176,7 @@ class helper_users:
@staticmethod
def get_super_user_list():
final_users = []
+ # pylint: disable=singleton-comparison
super_users = Users.select().where(Users.superuser == True)
for suser in super_users:
if suser.user_id not in final_users:
@@ -233,7 +233,7 @@ class helper_users:
@staticmethod
def add_user_roles(user: Union[dict, Users]):
- if type(user) == dict:
+ if isinstance(user, dict):
user_id = user['user_id']
else:
user_id = user.user_id
@@ -246,7 +246,7 @@ class helper_users:
for r in roles_query:
roles.add(r.role_id.role_id)
- if type(user) == dict:
+ if isinstance(user, dict):
user['roles'] = roles
else:
user.roles = roles
@@ -283,7 +283,12 @@ class helper_users:
return ApiKeys.get(ApiKeys.token_id == 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):
+ 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,
@@ -302,4 +307,4 @@ class helper_users:
-users_helper = helper_users()
\ No newline at end of file
+users_helper = helper_users()
diff --git a/app/classes/shared/cmd.py b/app/classes/shared/command.py
similarity index 78%
rename from app/classes/shared/cmd.py
rename to app/classes/shared/command.py
index d04eda30..001d8421 100644
--- a/app/classes/shared/cmd.py
+++ b/app/classes/shared/command.py
@@ -1,28 +1,16 @@
-import os
import sys
import cmd
import time
import threading
import logging
-from app.classes.shared.tasks import TasksManager
-
-logger = logging.getLogger(__name__)
-
from app.classes.shared.console import console
from app.classes.shared.helpers import helper
+
from app.classes.web.websocket_helper import websocket_helper
-try:
- import requests
-
-except ModuleNotFoundError as e:
- logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
- console.critical("Import Error: Unable to load {} module".format(e.name))
- sys.exit(1)
-
-
-class MainPrompt(cmd.Cmd, object):
+logger = logging.getLogger(__name__)
+class MainPrompt(cmd.Cmd):
def __init__(self, tasks_manager, migration_manager):
super().__init__()
@@ -30,16 +18,17 @@ class MainPrompt(cmd.Cmd, object):
self.migration_manager = migration_manager
# overrides the default Prompt
- prompt = "Crafty Controller v{} > ".format(helper.get_version_string())
+ prompt = f"Crafty Controller v{helper.get_version_string()} > "
@staticmethod
def emptyline():
pass
+ #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':
self.migration_manager.up()
@@ -52,9 +41,9 @@ class MainPrompt(cmd.Cmd, object):
elif line == 'diff':
console.info(self.migration_manager.diff)
elif line == 'info':
- console.info('Done: {}'.format(self.migration_manager.done))
- console.info('FS: {}'.format(self.migration_manager.todo))
- console.info('Todo: {}'.format(self.migration_manager.diff))
+ 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)
diff --git a/app/classes/shared/console.py b/app/classes/shared/console.py
index 18733adc..43b8cd92 100644
--- a/app/classes/shared/console.py
+++ b/app/classes/shared/console.py
@@ -9,13 +9,12 @@ try:
from termcolor import colored
except ModuleNotFoundError as e:
- logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
- print("Import Error: Unable to load {} module".format(e.name))
+ logger.critical(f"Import Error: Unable to load {e.name} module", exc_info=True)
+ print(f"Import Error: Unable to load {e.name} module")
from app.classes.shared.installer import installer
installer.do_install()
sys.exit(1)
-
class Console:
def __init__(self):
@@ -49,28 +48,27 @@ class Console:
def debug(self, message):
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
- self.magenta("[+] Crafty: {} - DEBUG:\t{}".format(dt, message))
+ self.magenta(f"[+] Crafty: {dt} - DEBUG:\t{message}")
def info(self, message):
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
- self.white("[+] Crafty: {} - INFO:\t{}".format(dt, message))
+ self.white(f"[+] Crafty: {dt} - INFO:\t{message}")
def warning(self, message):
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
- self.cyan("[+] Crafty: {} - WARNING:\t{}".format(dt, message))
+ self.cyan(f"[+] Crafty: {dt} - WARNING:\t{message}")
def error(self, message):
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
- self.yellow("[+] Crafty: {} - ERROR:\t{}".format(dt, message))
+ self.yellow(f"[+] Crafty: {dt} - ERROR:\t{message}")
def critical(self, message):
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
- self.red("[+] Crafty: {} - CRITICAL:\t{}".format(dt, message))
+ self.red(f"[+] Crafty: {dt} - CRITICAL:\t{message}")
def help(self, message):
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
- self.green("[+] Crafty: {} - HELP:\t{}".format(dt, message))
+ self.green(f"[+] Crafty: {dt} - HELP:\t{message}")
console = Console()
-
diff --git a/app/classes/shared/exceptions.py b/app/classes/shared/exceptions.py
index a5de82fa..05a46fdb 100644
--- a/app/classes/shared/exceptions.py
+++ b/app/classes/shared/exceptions.py
@@ -1,8 +1,8 @@
class CraftyException(Exception):
- pass
+ pass
class DatabaseException(CraftyException):
- pass
+ pass
class SchemaError(DatabaseException):
- pass
\ No newline at end of file
+ pass
diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py
index 939c5842..a4566df5 100644
--- a/app/classes/shared/helpers.py
+++ b/app/classes/shared/helpers.py
@@ -14,15 +14,13 @@ import html
import zipfile
import pathlib
import shutil
-from requests import get
-from contextlib import suppress
import ctypes
-import telnetlib
-from app.classes.web.websocket_helper import websocket_helper
-
from datetime import datetime
from socket import gethostname
+from contextlib import suppress
+from requests import get
+from app.classes.web.websocket_helper import websocket_helper
from app.classes.shared.console import console
logger = logging.getLogger(__name__)
@@ -32,9 +30,9 @@ try:
from OpenSSL import crypto
from argon2 import PasswordHasher
-except ModuleNotFoundError as e:
- logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
- console.critical("Import Error: Unable to load {} module".format(e.name))
+except ModuleNotFoundError as err:
+ logger.critical(f"Import Error: Unable to load {err.name} module", exc_info=True)
+ console.critical(f"Import Error: Unable to load {err.name} module")
sys.exit(1)
class Helpers:
@@ -68,8 +66,8 @@ class Helpers:
def check_file_perms(self, path):
try:
- fp = open(path, "r").close()
- logger.info("{} is readable".format(path))
+ open(path, "r", encoding='utf-8').close()
+ logger.info(f"{path} is readable")
return True
except PermissionError:
return False
@@ -82,7 +80,7 @@ class Helpers:
return True
else:
return False
- logger.error("{} does not exist".format(file))
+ logger.error(f"{file} does not exist")
return True
def get_servers_root_dir(self):
@@ -93,7 +91,7 @@ class Helpers:
try:
requests.get('https://google.com', timeout=1)
return True
- except Exception as err:
+ except Exception:
return False
@staticmethod
@@ -139,7 +137,7 @@ class Helpers:
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 == True: # 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:
@@ -154,11 +152,13 @@ class Helpers:
else:
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
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
cmd_out[ci] += c
@@ -167,20 +167,20 @@ class Helpers:
def get_setting(self, key, default_return=False):
try:
- with open(self.settings_file, "r") as f:
+ with open(self.settings_file, "r", encoding='utf-8') as f:
data = json.load(f)
if key in data.keys():
return data.get(key)
else:
- logger.error("Config File Error: setting {} does not exist".format(key))
- console.error("Config File Error: setting {} does not exist".format(key))
+ logger.error(f"Config File Error: setting {key} does not exist")
+ console.error(f"Config File Error: setting {key} does not exist")
return default_return
except Exception as e:
- logger.critical("Config File Error: Unable to read {} due to {}".format(self.settings_file, e))
- console.critical("Config File Error: Unable to read {} due to {}".format(self.settings_file, 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
@@ -199,11 +199,11 @@ class Helpers:
def get_version(self):
version_data = {}
try:
- with open(os.path.join(self.config_dir, 'version.json'), 'r') 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:
- console.critical("Unable to get version data!")
+ console.critical(f"Unable to get version data! \n{e}")
return version_data
@@ -217,7 +217,7 @@ class Helpers:
try:
data = json.loads(r.content)
except Exception as e:
- logger.error("Failed to load json content with error: {} ".format(e))
+ logger.error(f"Failed to load json content with error: {e}")
return data
@@ -225,11 +225,13 @@ class Helpers:
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', '?')
+
# set some defaults if we don't get version_data from our helper
- version = "{}.{}.{}-{}".format(version_data.get('major', '?'),
- version_data.get('minor', '?'),
- version_data.get('sub', '?'),
- version_data.get('meta', '?'))
+ version = f"{major}.{minor}.{sub}-{meta}"
return str(version)
def encode_pass(self, password):
@@ -240,7 +242,6 @@ class Helpers:
self.passhasher.verify(currenthash, password)
return True
except:
- pass
return False
def log_colors(self, line):
@@ -264,6 +265,7 @@ class Helpers:
# highlight users keywords
for keyword in user_keywords:
+ # pylint: disable=consider-using-f-string
search_replace = (r'({})'.format(keyword), r'\1')
replacements.append(search_replace)
@@ -274,7 +276,7 @@ class Helpers:
def validate_traversal(self, base_path, filename):
- logger.debug("Validating traversal (\"{x}\", \"{y}\")".format(x=base_path, y=filename))
+ logger.debug(f"Validating traversal (\"{base_path}\", \"{filename}\")")
base = pathlib.Path(base_path).resolve()
file = pathlib.Path(filename)
fileabs = base.joinpath(file).resolve()
@@ -287,8 +289,8 @@ class Helpers:
def tail_file(self, file_name, number_lines=20):
if not self.check_file_exists(file_name):
- logger.warning("Unable to find file to tail: {}".format(file_name))
- return ["Unable to find file to tail: {}".format(file_name)]
+ logger.warning(f"Unable to find file to tail: {file_name}")
+ return [f"Unable to find file to tail: {file_name}"]
# length of lines is X char here
avg_line_length = 255
@@ -297,7 +299,7 @@ class Helpers:
line_buffer = number_lines * avg_line_length
# open our file
- with open(file_name, 'r') as f:
+ with open(file_name, 'r', encoding='utf-8') as f:
# seek
f.seek(0, 2)
@@ -313,8 +315,7 @@ class Helpers:
lines = f.readlines()
except Exception as e:
- logger.warning('Unable to read a line in the file:{} - due to error: {}'.format(file_name, e))
- pass
+ 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
@@ -323,14 +324,14 @@ class Helpers:
def check_writeable(path: str):
filename = os.path.join(path, "tempfile.txt")
try:
- fp = open(filename, "w").close()
+ open(filename, "w", encoding='utf-8').close()
os.remove(filename)
- logger.info("{} is writable".format(filename))
+ logger.info(f"{filename} is writable")
return True
except Exception as e:
- logger.critical("Unable to write to {} - Error: {}".format(path, e))
+ logger.critical(f"Unable to write to {path} - Error: {e}")
return False
def checkRoot(self):
@@ -360,24 +361,17 @@ class Helpers:
try:
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(tempDir)
- for i in range(len(zip_ref.filelist)):
+ for i in enumerate(zip_ref.filelist):
if len(zip_ref.filelist) > 1 or not zip_ref.filelist[i].filename.endswith('/'):
- test = zip_ref.filelist[i].filename
break
- path_list = test.split('/')
- root_path = path_list[0]
- '''
- if len(path_list) > 1:
- for i in range(len(path_list) - 2):
- root_path = os.path.join(root_path, path_list[i + 1])
-'''
+
full_root_path = tempDir
for item in os.listdir(full_root_path):
try:
shutil.move(os.path.join(full_root_path, item), os.path.join(new_dir, item))
except Exception as ex:
- logger.error('ERROR IN ZIP IMPORT: {}'.format(ex))
+ logger.error(f'ERROR IN ZIP IMPORT: {ex}')
except Exception as ex:
print(ex)
else:
@@ -394,28 +388,28 @@ class Helpers:
# if not writeable, let's bomb out
if not writeable:
- logger.critical("Unable to write to {} directory!".format(self.root_dir))
+ logger.critical(f"Unable to write to {self.root_dir} directory!")
sys.exit(1)
- # ensure the log directory is there
+ # ensure the log directory is there
try:
with suppress(FileExistsError):
os.makedirs(os.path.join(self.root_dir, 'logs'))
except Exception as e:
- console.error("Failed to make logs directory with error: {} ".format(e))
+ console.error(f"Failed to make logs directory with error: {e} ")
# ensure the log file is there
try:
- open(log_file, 'a').close()
+ open(log_file, 'a', encoding='utf-8').close()
except Exception as e:
- console.critical("Unable to open log file!")
+ console.critical(f"Unable to open log file! {e}")
sys.exit(1)
# del any old session.lock file as this is a new session
try:
os.remove(session_log_file)
except Exception as e:
- logger.error("Deleting Session.lock failed with error: {} ".format(e))
+ logger.error(f"Deleting Session.lock failed with error: {e}")
@staticmethod
def get_time_as_string():
@@ -424,10 +418,10 @@ class Helpers:
@staticmethod
def check_file_exists(path: str):
- logger.debug('Looking for path: {}'.format(path))
+ logger.debug(f'Looking for path: {path}')
if os.path.exists(path) and os.path.isfile(path):
- logger.debug('Found path: {}'.format(path))
+ logger.debug(f'Found path: {path}')
return True
else:
return False
@@ -436,18 +430,20 @@ class Helpers:
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
return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0
+ # pylint: disable=consider-using-f-string
return "%.1f%s%s" % (num, 'Y', suffix)
@staticmethod
def check_path_exists(path: str):
if not path:
return False
- logger.debug('Looking for path: {}'.format(path))
+ logger.debug(f'Looking for path: {path}')
if os.path.exists(path):
- logger.debug('Found path: {}'.format(path))
+ logger.debug(f'Found path: {path}')
return True
else:
return False
@@ -459,17 +455,17 @@ class Helpers:
if os.path.exists(path) and os.path.isfile(path):
try:
- with open(path, 'r') as f:
+ with open(path, 'r', encoding='utf-8') as f:
for line in (f.readlines() [-lines:]):
contents = contents + line
return contents
except Exception as e:
- logger.error("Unable to read file: {}. \n Error: ".format(path, e))
+ logger.error(f"Unable to read file: {path}. \n Error: {e}")
return False
else:
- logger.error("Unable to read file: {}. File not found, or isn't a file.".format(path))
+ 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):
@@ -484,11 +480,10 @@ class Helpers:
data = json.loads(file_data)
pid = data.get('pid')
started = data.get('started')
- console.critical("Another Crafty Controller agent seems to be running...\npid: {} \nstarted on: {}".format(pid, started))
+ console.critical(f"Another Crafty Controller agent seems to be running...\npid: {pid} \nstarted on: {started}")
except Exception as e:
- logger.error("Failed to locate existing session.lock with error: {} ".format(e))
- console.error("Failed to locate existing session.lock with error: {} ".format(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} ")
sys.exit(1)
@@ -499,7 +494,7 @@ class Helpers:
'pid': pid,
'started': now.strftime("%d-%m-%Y, %H:%M:%S")
}
- with open(self.session_file, 'w') as f:
+ 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
@@ -526,14 +521,14 @@ class Helpers:
return sizes
@staticmethod
- def base64_encode_string(string: str):
- s_bytes = str(string).encode('utf-8')
+ def base64_encode_string(fun_str: str):
+ s_bytes = str(fun_str).encode('utf-8')
b64_bytes = base64.encodebytes(s_bytes)
return b64_bytes.decode('utf-8')
@staticmethod
- def base64_decode_string(string: str):
- s_bytes = str(string).encode('utf-8')
+ def base64_decode_string(fun_str: str):
+ s_bytes = str(fun_str).encode('utf-8')
b64_bytes = base64.decodebytes(s_bytes)
return b64_bytes.decode("utf-8")
@@ -553,7 +548,7 @@ class Helpers:
try:
os.makedirs(path)
- logger.debug("Created Directory : {}".format(path))
+ logger.debug(f"Created Directory : {path}")
# directory already exists - non-blocking error
except FileExistsError:
@@ -570,8 +565,8 @@ class Helpers:
cert_file = os.path.join(cert_dir, 'commander.cert.pem')
key_file = os.path.join(cert_dir, 'commander.key.pem')
- logger.info("SSL Cert File is set to: {}".format(cert_file))
- logger.info("SSL Key File is set to: {}".format(key_file))
+ logger.info(f"SSL Cert File is set to: {cert_file}")
+ logger.info(f"SSL Key File is set to: {key_file}")
# don't create new files if we already have them.
if self.check_file_exists(cert_file) and self.check_file_exists(key_file):
@@ -602,11 +597,11 @@ class Helpers:
cert.set_pubkey(k)
cert.sign(k, 'sha256')
- f = open(cert_file, "w")
+ f = open(cert_file, "w", encoding='utf-8')
f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode())
f.close()
- f = open(key_file, "w")
+ f = open(key_file, "w", encoding='utf-8')
f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, k).decode())
f.close()
@@ -645,7 +640,7 @@ class Helpers:
data = {}
if self.check_file_exists(default_file):
- with open(default_file, 'r') as f:
+ with open(default_file, 'r', encoding='utf-8') as f:
data = json.load(f)
del_json = helper.get_setting('delete_default_json')
@@ -662,25 +657,26 @@ class Helpers:
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 += \
- """
diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json
index df724cc8..3c94b2ce 100644
--- a/app/translations/en_EN.json
+++ b/app/translations/en_EN.json
@@ -281,7 +281,9 @@
"noDeleteFiles": "No, just remove from panel",
"sendingDelete": "Deleting Server",
"bePatientDelete": "Please be patient while we remove your server from the Crafty panel. This screen will close in a few moments.",
- "bePatientDeleteFiles" : "Please be patient while we remove your server from the Crafty panel and delete all files. This screen will close in a few moments."
+ "bePatientDeleteFiles" : "Please be patient while we remove your server from the Crafty panel and delete all files. This screen will close in a few moments.",
+ "crashTime": "Crash Timeout",
+ "crashTimeDesc": "How long should we wait before we consider your server as crashed?"
},
"serverConfigHelp": {
"title": "Server Config Area",
diff --git a/config_examples/nginx.conf.example b/config_examples/nginx.conf.example
index 37c56e38..eb3f428f 100644
--- a/config_examples/nginx.conf.example
+++ b/config_examples/nginx.conf.example
@@ -23,9 +23,11 @@ server {
ssl_certificate ;
ssl_certificate_key ;
location / {
+ #This is important for websockets
proxy_http_version 1.1;
proxy_redirect off;
+ #These are important for websockets. They are required for crafty to function properly.
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_set_header X-Forwarded-Proto https;
diff --git a/main.py b/main.py
index 50fa17ef..8252bc9b 100644
--- a/main.py
+++ b/main.py
@@ -1,4 +1,3 @@
-from cmd import Cmd
import os
import sys
import json
@@ -6,19 +5,23 @@ import time
import argparse
import logging.config
import signal
-import threading
-from app.classes.controllers.management_controller import Management_Controller
-
-""" Our custom classes / pip packages """
from app.classes.shared.console import console
from app.classes.shared.helpers import helper
+if helper.check_file_exists('/.dockerenv'):
+ console.cyan("Docker environment detected!")
+else:
+ if helper.checkRoot():
+ 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)
+# pylint: disable=wrong-import-position
from app.classes.shared.main_models import installer, database
-
from app.classes.shared.tasks import TasksManager
from app.classes.shared.main_controller import Controller
from app.classes.shared.migration import MigrationManager
-from app.classes.shared.cmd import MainPrompt
+from app.classes.shared.command import MainPrompt
def do_intro():
@@ -47,7 +50,7 @@ def setup_logging(debug=True):
if os.path.exists(logging_config_file):
# open our logging config file
- with open(logging_config_file, 'rt') 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'
@@ -56,11 +59,11 @@ def setup_logging(debug=True):
else:
logging.basicConfig(level=logging.DEBUG)
- logging.warning("Unable to read logging config from {}".format(logging_config_file))
- console.critical("Unable to read logging config from {}".format(logging_config_file))
+ logging.warning(f"Unable to read logging config from {logging_config_file}")
+ console.critical(f"Unable to read logging config from {logging_config_file}")
-""" Our Main Starter """
+# Our Main Starter
if __name__ == '__main__':
parser = argparse.ArgumentParser("Crafty Controller - A Server Management System")
@@ -81,21 +84,11 @@ if __name__ == '__main__':
args = parser.parse_args()
- if helper.check_file_exists('/.dockerenv'):
- console.cyan("Docker environment detected!")
- else:
- if helper.checkRoot():
- 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)
- helper.ensure_logging_setup()
-
setup_logging(debug=args.verbose)
# setting up the logger object
logger = logging.getLogger(__name__)
- console.cyan("Logging set to: {} ".format(logger.level))
+ console.cyan(f"Logging set to: {logger.level}")
# print our pretty start message
do_intro()
@@ -106,13 +99,14 @@ if __name__ == '__main__':
migration_manager = MigrationManager(database)
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, {}, through your router/firewall if you would like to be able to access Crafty remotely.".format(helper.get_setting('https_port')))
+ 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")
@@ -144,7 +138,8 @@ 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()
@@ -154,7 +149,7 @@ if __name__ == '__main__':
project_root = os.path.dirname(__file__)
controller.set_project_root(project_root)
- def sigterm_handler(signum, current_stack_frame):
+ def sigterm_handler():
print() # for newline
logger.info("Recieved SIGTERM, stopping Crafty")
console.info("Recieved SIGTERM, stopping Crafty")