diff --git a/CHANGELOG.md b/CHANGELOG.md
index 98e55169..e8ae3dfa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,14 @@
# Changelog
## --- [4.0.11] - 2022/TBD
### New features
-TBD
+- Add server import status indicators ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/433))
+- Users can now be assigned as manager of other users/roles ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/434))
+- Add variable shutdown timeouts ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/435))
### Bug fixes
-TBD
+- Fix creation quota not refilling after server delete ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/434))
### Tweaks
-TBD
+- Make imports threaded ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/433))
+- Add 'Created By' Field to servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/434))
### Lang
TBD
diff --git a/app/classes/controllers/crafty_perms_controller.py b/app/classes/controllers/crafty_perms_controller.py
index 9c79c33a..111e3971 100644
--- a/app/classes/controllers/crafty_perms_controller.py
+++ b/app/classes/controllers/crafty_perms_controller.py
@@ -60,26 +60,6 @@ class CraftyPermsController:
permissions_list = PermissionsCrafty.get_permissions(permissions_mask)
return permissions_list
- @staticmethod
- def add_server_creation(user_id):
- """Increase the "Server Creation" counter for this user
-
- Args:
- user_id (int): The modifiable user's ID
-
- Returns:
- int: The new count of servers created by this user
- """
- return PermissionsCrafty.add_server_creation(user_id)
-
- @staticmethod
- def add_user_creation(user_id):
- return PermissionsCrafty.add_user_creation(user_id)
-
- @staticmethod
- def add_role_creation(user_id):
- return PermissionsCrafty.add_role_creation(user_id)
-
@staticmethod
def get_api_key_permissions_list(key: ApiKeys):
return PermissionsCrafty.get_api_key_permissions_list(key)
diff --git a/app/classes/controllers/roles_controller.py b/app/classes/controllers/roles_controller.py
index 5e7925a3..ab5dcd5a 100644
--- a/app/classes/controllers/roles_controller.py
+++ b/app/classes/controllers/roles_controller.py
@@ -64,8 +64,8 @@ class RolesController:
HelperRoles.update_role(role_id, up_data)
@staticmethod
- def add_role(role_name):
- return HelperRoles.add_role(role_name)
+ def add_role(role_name, manager):
+ return HelperRoles.add_role(role_name, manager)
class RoleServerJsonType(t.TypedDict):
server_id: t.Union[str, int]
@@ -92,6 +92,7 @@ class RolesController:
def add_role_advanced(
name: str,
servers: t.Iterable[RoleServerJsonType],
+ manager: int,
) -> int:
"""Add a role with a name and a list of servers
@@ -102,7 +103,7 @@ class RolesController:
Returns:
int: The new role's ID
"""
- role_id: t.Final[int] = HelperRoles.add_role(name)
+ role_id: t.Final[int] = HelperRoles.add_role(name, manager)
for server in servers:
PermissionsServers.get_or_create(
role_id, server["server_id"], server["permissions"]
@@ -114,6 +115,7 @@ class RolesController:
role_id: t.Union[str, int],
role_name: t.Optional[str],
servers: t.Optional[t.Iterable[RoleServerJsonType]],
+ manager: int,
) -> None:
"""Update a role with a name and a list of servers
@@ -152,6 +154,7 @@ class RolesController:
up_data = {
"role_name": role_name,
"last_update": Helpers.get_time_as_string(),
+ "manager": manager,
}
# TODO: do the last_update on the db side
HelperRoles.update_role(role_id, up_data)
diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py
index 593e24d4..a0948769 100644
--- a/app/classes/controllers/servers_controller.py
+++ b/app/classes/controllers/servers_controller.py
@@ -52,6 +52,7 @@ class ServersController(metaclass=Singleton):
server_log_file: str,
server_stop: str,
server_type: str,
+ created_by: int,
server_port: int = 25565,
server_host: str = "127.0.0.1",
) -> int:
@@ -86,6 +87,7 @@ class ServersController(metaclass=Singleton):
server_log_file,
server_stop,
server_type,
+ created_by,
server_port,
server_host,
)
@@ -116,19 +118,19 @@ class ServersController(metaclass=Singleton):
return ret
@staticmethod
- def set_download(server_id):
+ def set_import(server_id):
srv = ServersController().get_server_instance_by_id(server_id)
- return srv.stats_helper.set_download()
+ return srv.stats_helper.set_import()
@staticmethod
- def finish_download(server_id):
+ def finish_import(server_id):
srv = ServersController().get_server_instance_by_id(server_id)
- return srv.stats_helper.finish_download()
+ return srv.stats_helper.finish_import()
@staticmethod
- def get_download_status(server_id):
+ def get_import_status(server_id):
server = ServersController().get_server_instance_by_id(server_id)
- return server.stats_helper.get_download_status()
+ return server.stats_helper.get_import_status()
def remove_server(self, server_id):
roles_list = PermissionsServers.get_roles_from_server(server_id)
diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py
index c3c90b2f..13b8fb4f 100644
--- a/app/classes/controllers/users_controller.py
+++ b/app/classes/controllers/users_controller.py
@@ -1,7 +1,9 @@
import logging
import typing as t
+from app.classes.models.servers import HelperServers
from app.classes.models.users import HelperUsers
+from app.classes.models.roles import HelperRoles
from app.classes.models.crafty_permissions import (
PermissionsCrafty,
EnumPermissionsCrafty,
@@ -132,6 +134,18 @@ class UsersController:
def set_support_path(user_id, support_path):
HelperUsers.set_support_path(user_id, support_path)
+ @staticmethod
+ def get_managed_users(exec_user_id):
+ return HelperUsers.get_managed_users(exec_user_id)
+
+ @staticmethod
+ def get_managed_roles(exec_user_id):
+ return HelperUsers.get_managed_roles(exec_user_id)
+
+ @staticmethod
+ def get_created_servers(exec_user_id):
+ return HelperServers.get_total_owned_servers(exec_user_id)
+
def update_user(self, user_id: str, user_data=None, user_crafty_data=None):
if user_crafty_data is None:
user_crafty_data = {}
@@ -206,6 +220,7 @@ class UsersController:
def add_user(
self,
username,
+ manager,
password,
email="default@example.com",
enabled: bool = True,
@@ -213,6 +228,7 @@ class UsersController:
):
return self.users_helper.add_user(
username,
+ manager,
password=password,
email=email,
enabled=enabled,
@@ -236,6 +252,10 @@ class UsersController:
)
def remove_user(self, user_id):
+ for user in self.get_managed_users(user_id):
+ self.update_user(user.user_id, {"manager": None})
+ for role in HelperUsers.get_managed_roles(user_id):
+ HelperRoles.update_role(role.role_id, {"manager": None})
return self.users_helper.remove_user(user_id)
@staticmethod
diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py
index a5eb11ba..1ecfc0f1 100644
--- a/app/classes/minecraft/serverjars.py
+++ b/app/classes/minecraft/serverjars.py
@@ -175,7 +175,7 @@ class ServerJars:
# we submit a db update for it's stats.
while True:
try:
- ServersController.set_download(server_id)
+ ServersController.set_import(server_id)
for user in server_users:
self.helper.websocket_helper.broadcast_user(
user, "send_start_reload", {}
@@ -190,7 +190,7 @@ class ServerJars:
try:
with open(path, "wb") as output:
shutil.copyfileobj(r.raw, output)
- ServersController.finish_download(server_id)
+ ServersController.finish_import(server_id)
for user in server_users:
self.helper.websocket_helper.broadcast_user(
@@ -203,7 +203,7 @@ class ServerJars:
return True
except Exception as e:
logger.error(f"Unable to save jar to {path} due to error:{e}")
- ServersController.finish_download(server_id)
+ ServersController.finish_import(server_id)
server_users = PermissionsServers.get_server_user_list(server_id)
for user in server_users:
self.helper.websocket_helper.broadcast_user(
diff --git a/app/classes/models/crafty_permissions.py b/app/classes/models/crafty_permissions.py
index 9b99bfb0..22383408 100644
--- a/app/classes/models/crafty_permissions.py
+++ b/app/classes/models/crafty_permissions.py
@@ -9,6 +9,7 @@ from peewee import (
)
from app.classes.models.base_model import BaseModel
+from app.classes.models.servers import HelperServers
from app.classes.models.users import Users, ApiKeys, HelperUsers
from app.classes.shared.permission_helper import PermissionHelper
@@ -23,9 +24,6 @@ class UserCrafty(BaseModel):
limit_server_creation = IntegerField(default=-1)
limit_user_creation = IntegerField(default=0)
limit_role_creation = IntegerField(default=0)
- created_server = IntegerField(default=0)
- created_user = IntegerField(default=0)
- created_role = IntegerField(default=0)
class Meta:
table_name = "user_crafty"
@@ -107,9 +105,6 @@ class PermissionsCrafty:
UserCrafty.limit_server_creation: 0,
UserCrafty.limit_user_creation: 0,
UserCrafty.limit_role_creation: 0,
- UserCrafty.created_server: 0,
- UserCrafty.created_user: 0,
- UserCrafty.created_role: 0,
}
).execute()
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
@@ -159,11 +154,16 @@ class PermissionsCrafty:
@staticmethod
def get_created_quantity_list(user_id):
- user_crafty = PermissionsCrafty.get_user_crafty(user_id)
quantity_list = {
- EnumPermissionsCrafty.SERVER_CREATION.name: user_crafty.created_server,
- EnumPermissionsCrafty.USER_CONFIG.name: user_crafty.created_user,
- EnumPermissionsCrafty.ROLES_CONFIG.name: user_crafty.created_role,
+ EnumPermissionsCrafty.SERVER_CREATION.name: HelperServers.get_total_owned_servers( # pylint: disable=line-too-long
+ user_id
+ ),
+ EnumPermissionsCrafty.USER_CONFIG.name: HelperUsers.get_managed_users(
+ user_id
+ ).count(),
+ EnumPermissionsCrafty.ROLES_CONFIG.name: HelperUsers.get_managed_roles(
+ user_id
+ ).count(),
}
return quantity_list
@@ -183,31 +183,6 @@ class PermissionsCrafty:
or limit_list[permission.name] == -1
)
- @staticmethod
- def add_server_creation(user_id: int):
- """Increase the "Server Creation" counter for this user
-
- Args:
- user_id (int): The modifiable user's ID
- """
- UserCrafty.update(created_server=UserCrafty.created_server + 1).where(
- UserCrafty.user_id == user_id
- ).execute()
-
- @staticmethod
- def add_user_creation(user_id):
- user_crafty = PermissionsCrafty.get_user_crafty(user_id)
- user_crafty.created_user += 1
- UserCrafty.save(user_crafty)
- return user_crafty.created_user
-
- @staticmethod
- def add_role_creation(user_id):
- user_crafty = PermissionsCrafty.get_user_crafty(user_id)
- user_crafty.created_role += 1
- UserCrafty.save(user_crafty)
- return user_crafty.created_role
-
@staticmethod
def get_api_key_permissions_list(key: ApiKeys):
user = HelperUsers.get_user(key.user_id)
diff --git a/app/classes/models/roles.py b/app/classes/models/roles.py
index 4d61e051..541f67e8 100644
--- a/app/classes/models/roles.py
+++ b/app/classes/models/roles.py
@@ -6,6 +6,7 @@ from peewee import (
DoesNotExist,
AutoField,
DateTimeField,
+ IntegerField,
)
from playhouse.shortcuts import model_to_dict
@@ -22,6 +23,7 @@ class Roles(BaseModel):
created = DateTimeField(default=datetime.datetime.now)
last_update = DateTimeField(default=datetime.datetime.now)
role_name = CharField(default="", unique=True, index=True)
+ manager = IntegerField(null=True)
class Meta:
table_name = "roles"
@@ -71,11 +73,12 @@ class HelperRoles:
)
@staticmethod
- def add_role(role_name):
+ def add_role(role_name, manager):
role_id = Roles.insert(
{
Roles.role_name: role_name.lower(),
Roles.created: Helpers.get_time_as_string(),
+ Roles.manager: manager,
}
).execute()
return role_id
diff --git a/app/classes/models/server_stats.py b/app/classes/models/server_stats.py
index 4c7091aa..6e589ffc 100644
--- a/app/classes/models/server_stats.py
+++ b/app/classes/models/server_stats.py
@@ -53,7 +53,7 @@ class ServerStats(Model):
waiting_start = BooleanField(default=False)
first_run = BooleanField(default=True)
crashed = BooleanField(default=False)
- downloading = BooleanField(default=False)
+ importing = BooleanField(default=False)
class Meta:
table_name = "server_stats"
@@ -215,26 +215,26 @@ class HelperServerStats:
ServerStats.server_id == self.server_id
).execute(self.database)
- def set_download(self):
+ def set_import(self):
# self.select_database(self.server_id)
- ServerStats.update(downloading=True).where(
+ ServerStats.update(importing=True).where(
ServerStats.server_id == self.server_id
).execute(self.database)
- def finish_download(self):
+ def finish_import(self):
# self.select_database(self.server_id)
- ServerStats.update(downloading=False).where(
+ ServerStats.update(importing=False).where(
ServerStats.server_id == self.server_id
).execute(self.database)
- def get_download_status(self):
+ def get_import_status(self):
# self.select_database(self.server_id)
- download_status = (
+ import_status = (
ServerStats.select()
.where(ServerStats.server_id == self.server_id)
.get(self.database)
)
- return download_status.downloading
+ return import_status.importing
def server_crash_reset(self):
if self.server_id is None:
@@ -257,7 +257,6 @@ class HelperServerStats:
def set_update(self, value):
if self.server_id is None:
return
-
# self.select_database(self.server_id)
try:
# Checks if server even exists
diff --git a/app/classes/models/servers.py b/app/classes/models/servers.py
index 71ca4851..69d05866 100644
--- a/app/classes/models/servers.py
+++ b/app/classes/models/servers.py
@@ -38,6 +38,8 @@ class Servers(BaseModel):
logs_delete_after = IntegerField(default=0)
type = CharField(default="minecraft-java")
show_status = BooleanField(default=1)
+ created_by = IntegerField(default=-100)
+ shutdown_timeout = IntegerField(default=60)
class Meta:
table_name = "servers"
@@ -64,6 +66,7 @@ class HelperServers:
server_log_file: str,
server_stop: str,
server_type: str,
+ created_by: int,
server_port: int = 25565,
server_host: str = "127.0.0.1",
) -> int:
@@ -105,6 +108,7 @@ class HelperServers:
Servers.stop_command: server_stop,
Servers.backup_path: backup_path,
Servers.type: server_type,
+ Servers.created_by: created_by,
}
).execute()
@@ -112,6 +116,10 @@ class HelperServers:
def get_server_obj(server_id):
return Servers.get_by_id(server_id)
+ @staticmethod
+ def get_total_owned_servers(user_id):
+ return Servers.select().where(Servers.created_by == user_id).count()
+
@staticmethod
def get_server_type_by_id(server_id):
server_type = Servers.select().where(Servers.server_id == server_id).get()
diff --git a/app/classes/models/users.py b/app/classes/models/users.py
index ac204e3c..0d8e596b 100644
--- a/app/classes/models/users.py
+++ b/app/classes/models/users.py
@@ -6,6 +6,7 @@ from peewee import (
ForeignKeyField,
CharField,
AutoField,
+ IntegerField,
DateTimeField,
BooleanField,
CompositeKey,
@@ -40,6 +41,7 @@ class Users(BaseModel):
server_order = CharField(default="")
preparing = BooleanField(default=False)
hints = BooleanField(default=True)
+ manager = IntegerField(default=None, null=True)
class Meta:
table_name = "users"
@@ -138,6 +140,16 @@ class HelperUsers:
user_query = Users.select().where(Users.user_id == user_id)
return user_query
+ @staticmethod
+ def get_managed_users(exec_user_id):
+ user_query = Users.select().where(Users.manager == exec_user_id)
+ return user_query
+
+ @staticmethod
+ def get_managed_roles(exec_user_id):
+ roles_query = Roles.select().where(Roles.manager == exec_user_id)
+ return roles_query
+
@staticmethod
def get_user(user_id):
if user_id == 0:
@@ -192,6 +204,7 @@ class HelperUsers:
def add_user(
self,
username: str,
+ manager: str,
password: str = None,
email: t.Optional[str] = None,
enabled: bool = True,
@@ -209,6 +222,7 @@ class HelperUsers:
Users.enabled: enabled,
Users.superuser: superuser,
Users.created: Helpers.get_time_as_string(),
+ Users.manager: manager,
}
).execute()
return user_id
@@ -229,6 +243,7 @@ class HelperUsers:
Users.enabled: enabled,
Users.superuser: superuser,
Users.created: Helpers.get_time_as_string(),
+ Users.manager: None,
}
).execute()
return user_id
diff --git a/app/classes/shared/file_helpers.py b/app/classes/shared/file_helpers.py
index 5cd38bbf..f9cde55a 100644
--- a/app/classes/shared/file_helpers.py
+++ b/app/classes/shared/file_helpers.py
@@ -27,10 +27,15 @@ class FileHelpers:
FileHelpers.del_dirs(sub)
else:
# Delete file if it is a file:
- sub.unlink()
-
- # This removes the top-level folder:
- path.rmdir()
+ try:
+ sub.unlink()
+ except:
+ logger.error(f"Unable to delete file {sub}")
+ try:
+ # This removes the top-level folder:
+ path.rmdir()
+ except:
+ logger.error("Unable to remove top level")
return True
@staticmethod
diff --git a/app/classes/shared/import_helper.py b/app/classes/shared/import_helper.py
new file mode 100644
index 00000000..769ebc3a
--- /dev/null
+++ b/app/classes/shared/import_helper.py
@@ -0,0 +1,216 @@
+import os
+import time
+import shutil
+import logging
+import threading
+
+from app.classes.controllers.server_perms_controller import PermissionsServers
+from app.classes.controllers.servers_controller import ServersController
+from app.classes.shared.helpers import Helpers
+from app.classes.shared.file_helpers import FileHelpers
+
+logger = logging.getLogger(__name__)
+
+
+class ImportHelpers:
+ allowed_quotes = ['"', "'", "`"]
+
+ def __init__(self, helper, file_helper):
+ self.file_helper: FileHelpers = file_helper
+ self.helper: Helpers = helper
+
+ def import_jar_server(self, server_path, new_server_dir, port, new_id):
+ import_thread = threading.Thread(
+ target=self.import_threaded_jar_server,
+ daemon=True,
+ args=(server_path, new_server_dir, port, new_id),
+ name=f"{new_id}_import",
+ )
+ import_thread.start()
+
+ def import_threaded_jar_server(self, server_path, new_server_dir, port, new_id):
+ for item in os.listdir(server_path):
+ if not item == "db_stats":
+ try:
+ if os.path.isdir(os.path.join(server_path, item)):
+ FileHelpers.copy_dir(
+ os.path.join(server_path, item),
+ os.path.join(new_server_dir, item),
+ )
+ else:
+ FileHelpers.copy_file(
+ os.path.join(server_path, item),
+ os.path.join(new_server_dir, item),
+ )
+ except shutil.Error as ex:
+ logger.error(f"Server import failed with error: {ex}")
+
+ has_properties = False
+ for item in os.listdir(new_server_dir):
+ if str(item) == "server.properties":
+ has_properties = True
+ if not has_properties:
+ logger.info(
+ f"No server.properties found on zip file import. "
+ f"Creating one with port selection of {str(port)}"
+ )
+ with open(
+ os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8"
+ ) as file:
+ file.write(f"server-port={port}")
+ file.close()
+ time.sleep(5)
+ ServersController.finish_import(new_id)
+ server_users = PermissionsServers.get_server_user_list(new_id)
+ for user in server_users:
+ self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {})
+
+ def import_java_zip_server(self, temp_dir, new_server_dir, port, new_id):
+ import_thread = threading.Thread(
+ target=self.import_threaded_java_zip_server,
+ daemon=True,
+ args=(temp_dir, new_server_dir, port, new_id),
+ name=f"{new_id}_java_zip_import",
+ )
+ import_thread.start()
+
+ def import_threaded_java_zip_server(self, temp_dir, new_server_dir, port, new_id):
+ has_properties = False
+ # extracts archive to temp directory
+ for item in os.listdir(temp_dir):
+ if str(item) == "server.properties":
+ has_properties = True
+ try:
+ if not os.path.isdir(os.path.join(temp_dir, item)):
+ FileHelpers.move_file(
+ os.path.join(temp_dir, item), os.path.join(new_server_dir, item)
+ )
+ else:
+ if item != "db_stats":
+ FileHelpers.move_dir(
+ os.path.join(temp_dir, item),
+ os.path.join(new_server_dir, item),
+ )
+ except Exception as ex:
+ logger.error(f"ERROR IN ZIP IMPORT: {ex}")
+ if not has_properties:
+ logger.info(
+ f"No server.properties found on zip file import. "
+ f"Creating one with port selection of {str(port)}"
+ )
+ with open(
+ os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8"
+ ) as file:
+ file.write(f"server-port={port}")
+ file.close()
+
+ server_users = PermissionsServers.get_server_user_list(new_id)
+ ServersController.finish_import(new_id)
+ for user in server_users:
+ self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {})
+ # deletes temp dir
+ FileHelpers.del_dirs(temp_dir)
+
+ def import_bedrock_server(
+ self, server_path, new_server_dir, port, full_jar_path, new_id
+ ):
+ import_thread = threading.Thread(
+ target=self.import_threaded_bedrock_server,
+ daemon=True,
+ args=(server_path, new_server_dir, port, full_jar_path, new_id),
+ name=f"{new_id}_bedrock_import",
+ )
+ import_thread.start()
+
+ def import_threaded_bedrock_server(
+ self, server_path, new_server_dir, port, full_jar_path, new_id
+ ):
+ for item in os.listdir(server_path):
+ if not item == "db_stats":
+ try:
+ if os.path.isdir(os.path.join(server_path, item)):
+ FileHelpers.copy_dir(
+ os.path.join(server_path, item),
+ os.path.join(new_server_dir, item),
+ )
+ else:
+ FileHelpers.copy_file(
+ os.path.join(server_path, item),
+ os.path.join(new_server_dir, item),
+ )
+ except shutil.Error as ex:
+ logger.error(f"Server import failed with error: {ex}")
+
+ has_properties = False
+ for item in os.listdir(new_server_dir):
+ if str(item) == "server.properties":
+ has_properties = True
+ if not has_properties:
+ logger.info(
+ f"No server.properties found on zip file import. "
+ f"Creating one with port selection of {str(port)}"
+ )
+ with open(
+ os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8"
+ ) as file:
+ file.write(f"server-port={port}")
+ file.close()
+ if os.name != "nt":
+ if Helpers.check_file_exists(full_jar_path):
+ os.chmod(full_jar_path, 0o2760)
+ ServersController.finish_import(new_id)
+ server_users = PermissionsServers.get_server_user_list(new_id)
+ for user in server_users:
+ self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {})
+
+ def import_bedrock_zip_server(
+ self, temp_dir, new_server_dir, full_jar_path, port, new_id
+ ):
+ import_thread = threading.Thread(
+ target=self.import_threaded_bedrock_zip_server,
+ daemon=True,
+ args=(temp_dir, new_server_dir, full_jar_path, port, new_id),
+ name=f"{new_id}_bedrock_import",
+ )
+ import_thread.start()
+
+ def import_threaded_bedrock_zip_server(
+ self, temp_dir, new_server_dir, full_jar_path, port, new_id
+ ):
+ has_properties = False
+ # extracts archive to temp directory
+ for item in os.listdir(temp_dir):
+ if str(item) == "server.properties":
+ has_properties = True
+ try:
+ if not os.path.isdir(os.path.join(temp_dir, item)):
+ FileHelpers.move_file(
+ os.path.join(temp_dir, item), os.path.join(new_server_dir, item)
+ )
+ else:
+ if item != "db_stats":
+ FileHelpers.move_dir(
+ os.path.join(temp_dir, item),
+ os.path.join(new_server_dir, item),
+ )
+ except Exception as ex:
+ logger.error(f"ERROR IN ZIP IMPORT: {ex}")
+ if not has_properties:
+ logger.info(
+ f"No server.properties found on zip file import. "
+ f"Creating one with port selection of {str(port)}"
+ )
+ with open(
+ os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8"
+ ) as file:
+ file.write(f"server-port={port}")
+ file.close()
+ ServersController.finish_import(new_id)
+ server_users = PermissionsServers.get_server_user_list(new_id)
+ for user in server_users:
+ self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {})
+ if os.name != "nt":
+ if Helpers.check_file_exists(full_jar_path):
+ os.chmod(full_jar_path, 0o2760)
+ # deletes temp dir
+ FileHelpers.del_dirs(temp_dir)
diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py
index bca24fe8..d52ef5e5 100644
--- a/app/classes/shared/main_controller.py
+++ b/app/classes/shared/main_controller.py
@@ -28,15 +28,17 @@ from app.classes.shared.authentication import Authentication
from app.classes.shared.console import Console
from app.classes.shared.helpers import Helpers
from app.classes.shared.file_helpers import FileHelpers
+from app.classes.shared.import_helper import ImportHelpers
from app.classes.minecraft.serverjars import ServerJars
logger = logging.getLogger(__name__)
class Controller:
- def __init__(self, database, helper, file_helper):
+ def __init__(self, database, helper, file_helper, import_helper):
self.helper: Helpers = helper
self.file_helper: FileHelpers = file_helper
+ self.import_helper: ImportHelpers = import_helper
self.server_jars: ServerJars = ServerJars(helper)
self.users_helper: HelperUsers = HelperUsers(database, self.helper)
self.roles_helper: HelperRoles = HelperRoles(database)
@@ -244,7 +246,7 @@ class Controller:
except:
return {"percent": 0, "total_files": 0}
- def create_api_server(self, data: dict):
+ def create_api_server(self, data: dict, user_id):
server_fs_uuid = Helpers.create_uuid()
new_server_path = os.path.join(self.helper.servers_dir, server_fs_uuid)
backup_path = os.path.join(self.helper.backup_path, server_fs_uuid)
@@ -307,7 +309,9 @@ class Controller:
# TODO: Copy files from the zip file to the new server directory
server_file = create_data["jarfile"]
raise Exception("Not yet implemented")
- _create_server_properties_if_needed(create_data["server_properties_port"])
+ _create_server_properties_if_needed(
+ create_data["server_properties_port"],
+ )
min_mem = create_data["mem_min"]
max_mem = create_data["mem_max"]
@@ -403,6 +407,7 @@ class Controller:
server_log_file=log_location,
server_stop=stop_command,
server_port=monitoring_port,
+ created_by=user_id,
server_host=monitoring_host,
server_type=monitoring_type,
)
@@ -429,6 +434,7 @@ class Controller:
min_mem: int,
max_mem: int,
port: int,
+ user_id: int,
):
server_id = Helpers.create_uuid()
server_dir = os.path.join(self.helper.servers_dir, server_id)
@@ -489,6 +495,7 @@ class Controller:
server_log_file,
server_stop,
port,
+ user_id,
server_type="minecraft-java",
)
@@ -524,6 +531,7 @@ class Controller:
min_mem: int,
max_mem: int,
port: int,
+ user_id: int,
):
server_id = Helpers.create_uuid()
new_server_dir = os.path.join(self.helper.servers_dir, server_id)
@@ -537,25 +545,6 @@ class Controller:
Helpers.ensure_dir_exists(new_server_dir)
Helpers.ensure_dir_exists(backup_path)
server_path = Helpers.get_os_understandable_path(server_path)
- try:
- FileHelpers.copy_dir(server_path, new_server_dir, True)
- except shutil.Error as ex:
- logger.error(f"Server import failed with error: {ex}")
-
- has_properties = False
- for item in os.listdir(new_server_dir):
- if str(item) == "server.properties":
- has_properties = True
- if not has_properties:
- logger.info(
- f"No server.properties found on zip file import. "
- f"Creating one with port selection of {str(port)}"
- )
- with open(
- os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8"
- ) as file:
- file.write(f"server-port={port}")
- file.close()
full_jar_path = os.path.join(new_server_dir, server_jar)
@@ -584,8 +573,11 @@ class Controller:
server_log_file,
server_stop,
port,
+ user_id,
server_type="minecraft-java",
)
+ ServersController.set_import(new_id)
+ self.import_helper.import_jar_server(server_path, new_server_dir, port, new_id)
return new_id
def import_zip_server(
@@ -596,6 +588,7 @@ class Controller:
min_mem: int,
max_mem: int,
port: int,
+ user_id: int,
):
server_id = Helpers.create_uuid()
new_server_dir = os.path.join(self.helper.servers_dir, server_id)
@@ -609,32 +602,6 @@ class Controller:
temp_dir = Helpers.get_os_understandable_path(zip_path)
Helpers.ensure_dir_exists(new_server_dir)
Helpers.ensure_dir_exists(backup_path)
- has_properties = False
- # extracts archive to temp directory
- for item in os.listdir(temp_dir):
- if str(item) == "server.properties":
- has_properties = True
- try:
- if not os.path.isdir(os.path.join(temp_dir, item)):
- FileHelpers.move_file(
- os.path.join(temp_dir, item), os.path.join(new_server_dir, item)
- )
- else:
- FileHelpers.move_dir(
- os.path.join(temp_dir, item), os.path.join(new_server_dir, item)
- )
- except Exception as ex:
- logger.error(f"ERROR IN ZIP IMPORT: {ex}")
- if not has_properties:
- logger.info(
- f"No server.properties found on zip file import. "
- f"Creating one with port selection of {str(port)}"
- )
- with open(
- os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8"
- ) as file:
- file.write(f"server-port={port}")
- file.close()
full_jar_path = os.path.join(new_server_dir, server_jar)
@@ -664,8 +631,13 @@ class Controller:
server_log_file,
server_stop,
port,
+ user_id,
server_type="minecraft-java",
)
+ ServersController.set_import(new_id)
+ self.import_helper.import_java_zip_server(
+ temp_dir, new_server_dir, port, new_id
+ )
return new_id
# **********************************************************************************
@@ -673,7 +645,12 @@ class Controller:
# **********************************************************************************
def import_bedrock_server(
- self, server_name: str, server_path: str, server_exe: str, port: int
+ self,
+ server_name: str,
+ server_path: str,
+ server_exe: str,
+ port: int,
+ user_id: int,
):
server_id = Helpers.create_uuid()
new_server_dir = os.path.join(self.helper.servers_dir, server_id)
@@ -687,25 +664,6 @@ class Controller:
Helpers.ensure_dir_exists(new_server_dir)
Helpers.ensure_dir_exists(backup_path)
server_path = Helpers.get_os_understandable_path(server_path)
- try:
- FileHelpers.copy_dir(server_path, new_server_dir, True)
- except shutil.Error as ex:
- logger.error(f"Server import failed with error: {ex}")
-
- has_properties = False
- for item in os.listdir(new_server_dir):
- if str(item) == "server.properties":
- has_properties = True
- if not has_properties:
- logger.info(
- f"No server.properties found on zip file import. "
- f"Creating one with port selection of {str(port)}"
- )
- with open(
- os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8"
- ) as file:
- file.write(f"server-port={port}")
- file.close()
full_jar_path = os.path.join(new_server_dir, server_exe)
@@ -727,15 +685,22 @@ class Controller:
server_log_file,
server_stop,
port,
+ user_id,
server_type="minecraft-bedrock",
)
- if os.name != "nt":
- if Helpers.check_file_exists(full_jar_path):
- os.chmod(full_jar_path, 0o2760)
+ ServersController.set_import(new_id)
+ self.import_helper.import_bedrock_server(
+ server_path, new_server_dir, port, full_jar_path, new_id
+ )
return new_id
def import_bedrock_zip_server(
- self, server_name: str, zip_path: str, server_exe: str, port: int
+ self,
+ server_name: str,
+ zip_path: str,
+ server_exe: str,
+ port: int,
+ user_id: int,
):
server_id = Helpers.create_uuid()
new_server_dir = os.path.join(self.helper.servers_dir, server_id)
@@ -749,32 +714,6 @@ class Controller:
temp_dir = Helpers.get_os_understandable_path(zip_path)
Helpers.ensure_dir_exists(new_server_dir)
Helpers.ensure_dir_exists(backup_path)
- has_properties = False
- # extracts archive to temp directory
- for item in os.listdir(temp_dir):
- if str(item) == "server.properties":
- has_properties = True
- try:
- if not os.path.isdir(os.path.join(temp_dir, item)):
- FileHelpers.move_file(
- os.path.join(temp_dir, item), os.path.join(new_server_dir, item)
- )
- else:
- FileHelpers.move_dir(
- os.path.join(temp_dir, item), os.path.join(new_server_dir, item)
- )
- except Exception as ex:
- logger.error(f"ERROR IN ZIP IMPORT: {ex}")
- if not has_properties:
- logger.info(
- f"No server.properties found on zip file import. "
- f"Creating one with port selection of {str(port)}"
- )
- with open(
- os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8"
- ) as file:
- file.write(f"server-port={port}")
- file.close()
full_jar_path = os.path.join(new_server_dir, server_exe)
@@ -796,8 +735,12 @@ class Controller:
server_log_file,
server_stop,
port,
+ user_id,
server_type="minecraft-bedrock",
)
+ self.import_helper.import_bedrock_zip_server(
+ temp_dir, new_server_dir, full_jar_path, port, new_id
+ )
if os.name != "nt":
if Helpers.check_file_exists(full_jar_path):
os.chmod(full_jar_path, 0o2760)
@@ -838,6 +781,7 @@ class Controller:
server_log_file: str,
server_stop: str,
server_port: int,
+ created_by: int,
server_type: str,
server_host: str = "127.0.0.1",
):
@@ -852,6 +796,7 @@ class Controller:
server_log_file,
server_stop,
server_type,
+ created_by,
server_port,
server_host,
)
diff --git a/app/classes/shared/main_models.py b/app/classes/shared/main_models.py
index 73d1d484..7c43a131 100644
--- a/app/classes/shared/main_models.py
+++ b/app/classes/shared/main_models.py
@@ -26,6 +26,7 @@ class DatabaseBuilder:
password=password,
email="default@example.com",
superuser=True,
+ manager=None,
)
def is_fresh_install(self):
diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py
index 74e93ba7..0b2c5b96 100644
--- a/app/classes/shared/server.py
+++ b/app/classes/shared/server.py
@@ -293,7 +293,7 @@ class ServerInstance:
else:
user_lang = HelperUsers.get_user_lang_by_id(user_id)
- if self.stats_helper.get_download_status():
+ if self.stats_helper.get_import_status():
if user_id:
self.helper.websocket_helper.broadcast_user(
user_id,
@@ -612,21 +612,25 @@ class ServerInstance:
# caching the name and pid number
server_name = self.name
server_pid = self.process.pid
+ self.shutdown_timeout = self.settings["shutdown_timeout"]
while running:
i += 1
- logstr = (
- f"Server {server_name} is still running "
- f"- waiting 2s to see if it stops ({int(60-(i*2))} "
- f"seconds until force close)"
- )
- logger.info(logstr)
- Console.info(logstr)
+ ttk = int(self.shutdown_timeout - (i * 2))
+ if i <= self.shutdown_timeout / 2:
+ logstr = (
+ f"Server {server_name} is still running "
+ "- waiting 2s to see if it stops"
+ f"({ttk} "
+ f"seconds until force close)"
+ )
+ logger.info(logstr)
+ Console.info(logstr)
running = self.check_running()
time.sleep(2)
# if we haven't closed in 60 seconds, let's just slam down on the PID
- if i >= 30:
+ if i >= round(self.shutdown_timeout / 2, 0):
logger.info(
f"Server {server_name} is still running - Forcing the process down"
)
diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py
index 9aa41d28..bd8f9424 100644
--- a/app/classes/web/ajax_handler.py
+++ b/app/classes/web/ajax_handler.py
@@ -394,6 +394,7 @@ class AjaxHandler(BaseHandler):
"1",
"2",
server_data["server_port"],
+ server_data["created_by"],
)
new_server_id = new_server
new_server = self.controller.servers.get_server_data(new_server)
@@ -416,6 +417,7 @@ class AjaxHandler(BaseHandler):
temp_dir,
server_data["executable"],
server_data["server_port"],
+ server_data["created_by"],
)
new_server_id = new_server
new_server = self.controller.servers.get_server_data(new_server)
diff --git a/app/classes/web/api_handler.py b/app/classes/web/api_handler.py
index 43af4ae8..34b09ee8 100644
--- a/app/classes/web/api_handler.py
+++ b/app/classes/web/api_handler.py
@@ -340,10 +340,11 @@ class CreateUser(ApiHandler):
new_username = self.get_argument("username").lower()
new_pass = self.get_argument("password")
+ manager = int(user_obj["user_id"])
if new_username:
self.controller.users.add_user(
- new_username, new_pass, "default@example.com", True, False
+ new_username, manager, new_pass, "default@example.com", True, False
)
self.return_response(
diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py
index e8592cdd..a8bac6e2 100644
--- a/app/classes/web/panel_handler.py
+++ b/app/classes/web/panel_handler.py
@@ -453,8 +453,8 @@ class PanelHandler(BaseHandler):
for server in un_used_servers[:]:
if flag == 0:
server["stats"][
- "downloading"
- ] = self.controller.servers.get_download_status(
+ "importing"
+ ] = self.controller.servers.get_import_status(
str(server["stats"]["server_id"]["server_id"])
)
server["stats"]["crashed"] = self.controller.servers.is_crashed(
@@ -572,11 +572,11 @@ class PanelHandler(BaseHandler):
"started": "False",
}
if not self.failed_server:
- page_data["downloading"] = self.controller.servers.get_download_status(
+ page_data["importing"] = self.controller.servers.get_import_status(
server_id
)
else:
- page_data["downloading"] = False
+ page_data["importing"] = False
page_data["server_id"] = server_id
try:
page_data["waiting_start"] = self.controller.servers.get_waiting_start(
@@ -866,6 +866,18 @@ class PanelHandler(BaseHandler):
page_data["users"] = self.controller.users.get_all_users()
page_data["roles"] = self.controller.roles.get_all_roles()
page_data["auth-servers"][user.user_id] = super_auth_servers
+ page_data["managed_users"] = []
+ else:
+ page_data["managed_users"] = self.controller.users.get_managed_users(
+ exec_user["user_id"]
+ )
+ page_data["assigned_roles"] = []
+ for item in page_data["roles"]:
+ page_data["assigned_roles"].append(item.role_id)
+
+ page_data["managed_roles"] = self.controller.users.get_managed_roles(
+ exec_user["user_id"]
+ )
template = "panel/panel_config.html"
@@ -891,7 +903,7 @@ class PanelHandler(BaseHandler):
)
return
- page_data["roles_all"] = self.controller.roles.get_all_roles()
+ page_data["roles"] = self.controller.roles.get_all_roles()
page_data["servers"] = []
page_data["servers_all"] = self.controller.servers.get_all_defined_servers()
page_data["role-servers"] = []
@@ -910,8 +922,16 @@ class PanelHandler(BaseHandler):
)
if superuser:
page_data["super-disabled"] = ""
+ page_data["users"] = self.controller.users.get_all_users()
else:
page_data["super-disabled"] = "disabled"
+
+ page_data["exec_user"] = exec_user["user_id"]
+
+ page_data["manager"] = {
+ "user_id": -100,
+ "username": "None",
+ }
for file in sorted(
os.listdir(os.path.join(self.helper.root_dir, "app", "translations"))
):
@@ -1080,9 +1100,21 @@ class PanelHandler(BaseHandler):
page_data["user"] = self.controller.users.get_user_by_id(user_id)
page_data["servers"] = set()
page_data["role-servers"] = page_role_servers
- page_data["roles_all"] = self.controller.roles.get_all_roles()
+ page_data["roles"] = self.controller.roles.get_all_roles()
+ page_data["exec_user"] = exec_user["user_id"]
page_data["servers_all"] = self.controller.servers.get_all_defined_servers()
page_data["superuser"] = superuser
+ if page_data["user"]["manager"] is not None:
+ page_data["manager"] = self.controller.users.get_user_by_id(
+ page_data["user"]["manager"]
+ )
+ else:
+ page_data["manager"] = {
+ "user_id": -100,
+ "username": "None",
+ }
+ if exec_user["superuser"]:
+ page_data["users"] = self.controller.users.get_all_users()
page_data[
"permissions_all"
] = self.controller.crafty_perms.list_defined_crafty_permissions()
@@ -1121,6 +1153,17 @@ class PanelHandler(BaseHandler):
"/panel/error?error=Unauthorized access: not a user editor"
)
return
+ if (
+ (
+ self.controller.users.get_user_by_id(user_id)["manager"]
+ != exec_user["user_id"]
+ )
+ and not exec_user["superuser"]
+ and str(exec_user["user_id"]) != str(user_id)
+ ):
+ self.redirect(
+ "/panel/error?error=Unauthorized access: you cannot edit this user"
+ )
page_data["servers"] = []
page_data["role-servers"] = []
@@ -1218,6 +1261,11 @@ class PanelHandler(BaseHandler):
defined_servers = self.controller.servers.get_authorized_servers(
exec_user["user_id"]
)
+
+ page_data["role_manager"] = {
+ "user_id": -100,
+ "username": "None",
+ }
page_servers = []
for server in defined_servers:
if server not in page_servers:
@@ -1235,6 +1283,7 @@ class PanelHandler(BaseHandler):
user_roles = self.get_user_roles()
page_data["new_role"] = False
role_id = self.get_argument("id", None)
+ role = self.controller.roles.get_role(role_id)
page_data["role"] = self.controller.roles.get_role_with_servers(role_id)
if exec_user["superuser"]:
defined_servers = self.controller.servers.list_defined_servers()
@@ -1258,7 +1307,21 @@ class PanelHandler(BaseHandler):
page_data["user-roles"] = user_roles
page_data["users"] = self.controller.users.get_all_users()
- if EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions:
+ if page_data["role"]["manager"] is not None:
+ page_data["role_manager"] = self.controller.users.get_user_by_id(
+ page_data["role"]["manager"]
+ )
+ else:
+ page_data["role_manager"] = {
+ "user_id": -100,
+ "username": "None",
+ }
+
+ if (
+ EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions
+ or exec_user["user_id"] != role["manager"]
+ and not exec_user["superuser"]
+ ):
self.redirect(
"/panel/error?error=Unauthorized access: not a role editor"
)
@@ -1272,8 +1335,15 @@ class PanelHandler(BaseHandler):
elif page == "remove_role":
role_id = bleach.clean(self.get_argument("id", None))
- if not superuser:
- self.redirect("/panel/error?error=Unauthorized access: not superuser")
+ if (
+ not superuser
+ and self.controller.roles.get_role(role_id)["manager"]
+ != exec_user["user_id"]
+ ):
+ self.redirect(
+ "/panel/error?error=Unauthorized access: not superuser not"
+ " role manager"
+ )
return
if role_id is None:
self.redirect("/panel/error?error=Invalid Role ID")
@@ -1424,6 +1494,7 @@ class PanelHandler(BaseHandler):
return
server_name = self.get_argument("server_name", None)
server_obj = self.controller.servers.get_server_obj(server_id)
+ shutdown_timeout = self.get_argument("shutdown_timeout", 60)
if superuser:
server_path = self.get_argument("server_path", None)
if Helpers.is_os_windows():
@@ -1504,6 +1575,7 @@ class PanelHandler(BaseHandler):
)
server_obj.server_name = server_name
+ server_obj.shutdown_timeout = shutdown_timeout
if superuser:
if Helpers.validate_traversal(
self.helper.get_servers_root_dir(), server_path
@@ -1936,6 +2008,7 @@ class PanelHandler(BaseHandler):
"system user is not editable"
)
user_id = bleach.clean(self.get_argument("id", None))
+ user = self.controller.users.get_user_by_id(user_id)
username = bleach.clean(self.get_argument("username", None).lower())
if (
username != self.controller.users.get_user_by_id(user_id)["username"]
@@ -1968,7 +2041,19 @@ class PanelHandler(BaseHandler):
else:
superuser = 0
- if not exec_user["superuser"]:
+ if exec_user["superuser"]:
+ manager = self.get_argument("manager")
+ if manager == "":
+ manager = None
+ else:
+ manager = int(manager)
+ else:
+ manager = user["manager"]
+
+ if (
+ not exec_user["superuser"]
+ and int(exec_user["user_id"]) != user["manager"]
+ ):
if username is None or username == "":
self.redirect("/panel/error?error=Invalid username")
return
@@ -2019,6 +2104,7 @@ class PanelHandler(BaseHandler):
user_data = {
"username": username,
+ "manager": manager,
"password": password0,
"email": email,
"enabled": enabled,
@@ -2035,13 +2121,13 @@ class PanelHandler(BaseHandler):
user_id, user_data=user_data, user_crafty_data=user_crafty_data
)
- self.controller.management.add_to_audit_log(
- exec_user["user_id"],
- f"Edited user {username} (UID:{user_id}) with roles {roles} "
- f"and permissions {permissions_mask}",
- server_id=0,
- source_ip=self.get_remote_ip(),
- )
+ self.controller.management.add_to_audit_log(
+ exec_user["user_id"],
+ f"Edited user {username} (UID:{user_id}) with roles {roles} "
+ f"and permissions {permissions_mask}",
+ server_id=0,
+ source_ip=self.get_remote_ip(),
+ )
self.redirect("/panel/panel_config")
elif page == "edit_user_apikeys":
@@ -2164,6 +2250,15 @@ class PanelHandler(BaseHandler):
if username is None or username == "":
self.redirect("/panel/error?error=Invalid username")
return
+
+ if exec_user["superuser"]:
+ manager = self.get_argument("manager")
+ if manager == "":
+ manager = None
+ else:
+ manager = int(manager)
+ else:
+ manager = int(exec_user["user_id"])
# does this user id exist?
if self.controller.users.get_id_by_name(username) is not None:
self.redirect("/panel/error?error=User exists")
@@ -2178,6 +2273,7 @@ class PanelHandler(BaseHandler):
user_id = self.controller.users.add_user(
username,
+ manager=manager,
password=password0,
email=email,
enabled=enabled,
@@ -2204,14 +2300,19 @@ class PanelHandler(BaseHandler):
server_id=0,
source_ip=self.get_remote_ip(),
)
- self.controller.crafty_perms.add_user_creation(exec_user["user_id"])
self.redirect("/panel/panel_config")
elif page == "edit_role":
role_id = bleach.clean(self.get_argument("id", None))
role_name = bleach.clean(self.get_argument("role_name", None))
- if EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions:
+ role = self.controller.roles.get_role(role_id)
+
+ if (
+ EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions
+ and exec_user["user_id"] != role["manager"]
+ and not exec_user["superuser"]
+ ):
self.redirect(
"/panel/error?error=Unauthorized access: not a role editor"
)
@@ -2227,9 +2328,18 @@ class PanelHandler(BaseHandler):
self.redirect("/panel/error?error=Invalid Role ID")
return
+ if exec_user["superuser"]:
+ manager = self.get_argument("manager", None)
+ if manager == "":
+ manager = None
+ else:
+ manager = role["manager"]
+
servers = self.get_role_servers()
- self.controller.roles.update_role_advanced(role_id, role_name, servers)
+ self.controller.roles.update_role_advanced(
+ role_id, role_name, servers, manager
+ )
self.controller.management.add_to_audit_log(
exec_user["user_id"],
@@ -2241,6 +2351,12 @@ class PanelHandler(BaseHandler):
elif page == "add_role":
role_name = bleach.clean(self.get_argument("role_name", None))
+ if exec_user["superuser"]:
+ manager = self.get_argument("manager", None)
+ if manager == "":
+ manager = None
+ else:
+ manager = exec_user["user_id"]
if EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions:
self.redirect(
@@ -2265,7 +2381,9 @@ class PanelHandler(BaseHandler):
servers = self.get_role_servers()
- role_id = self.controller.roles.add_role_advanced(role_name, servers)
+ role_id = self.controller.roles.add_role_advanced(
+ role_name, servers, manager
+ )
self.controller.management.add_to_audit_log(
exec_user["user_id"],
@@ -2273,7 +2391,6 @@ class PanelHandler(BaseHandler):
server_id=0,
source_ip=self.get_remote_ip(),
)
- self.controller.crafty_perms.add_role_creation(exec_user["user_id"])
self.redirect("/panel/panel_config")
else:
diff --git a/app/classes/web/routes/api/roles/index.py b/app/classes/web/routes/api/roles/index.py
index 2ca1baf3..150bff0c 100644
--- a/app/classes/web/routes/api/roles/index.py
+++ b/app/classes/web/routes/api/roles/index.py
@@ -116,7 +116,9 @@ class ApiRolesIndexHandler(BaseApiHandler):
400, {"status": "error", "error": "ROLE_NAME_ALREADY_EXISTS"}
)
- role_id = self.controller.roles.add_role_advanced(role_name, servers)
+ role_id = self.controller.roles.add_role_advanced(
+ role_name, servers, user["user_id"]
+ )
self.controller.management.add_to_audit_log(
user["user_id"],
diff --git a/app/classes/web/routes/api/servers/index.py b/app/classes/web/routes/api/servers/index.py
index 7db12f45..b94b4c01 100644
--- a/app/classes/web/routes/api/servers/index.py
+++ b/app/classes/web/routes/api/servers/index.py
@@ -665,10 +665,9 @@ class ApiServersIndexHandler(BaseApiHandler):
},
)
- new_server_id, new_server_uuid = self.controller.create_api_server(data)
-
- # Increase the server creation counter
- self.controller.crafty_perms.add_server_creation(user["user_id"])
+ new_server_id, new_server_uuid = self.controller.create_api_server(
+ data, user["user_id"]
+ )
self.controller.servers.stats.record_stats()
diff --git a/app/classes/web/routes/api/servers/server/action.py b/app/classes/web/routes/api/servers/server/action.py
index cf9163b9..e5b3ae23 100644
--- a/app/classes/web/routes/api/servers/server/action.py
+++ b/app/classes/web/routes/api/servers/server/action.py
@@ -84,6 +84,7 @@ class ApiServersServerActionHandler(BaseApiHandler):
new_server_log_file,
server_data.get("stop_command"),
server_data.get("type"),
+ user_id,
server_data.get("server_port"),
)
diff --git a/app/classes/web/routes/api/users/index.py b/app/classes/web/routes/api/users/index.py
index 3e4cfdab..6f46740e 100644
--- a/app/classes/web/routes/api/users/index.py
+++ b/app/classes/web/routes/api/users/index.py
@@ -96,6 +96,7 @@ class ApiUsersIndexHandler(BaseApiHandler):
username = data["username"]
username = str(username).lower()
+ manager = int(user["user_id"])
password = data["password"]
email = data.get("email", "default@example.com")
enabled = data.get("enabled", True)
@@ -149,6 +150,7 @@ class ApiUsersIndexHandler(BaseApiHandler):
# TODO: do this in the most efficient way
user_id = self.controller.users.add_user(
username,
+ manager,
password,
email,
enabled,
diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py
index df4ba684..c13198ed 100644
--- a/app/classes/web/server_handler.py
+++ b/app/classes/web/server_handler.py
@@ -1,6 +1,7 @@
import json
import logging
import os
+import time
import tornado.web
import tornado.escape
import bleach
@@ -224,6 +225,23 @@ class ServerHandler(BaseHandler):
if server_id is not None:
if command == "clone_server":
+ if (
+ not superuser
+ and not self.controller.crafty_perms.can_create_server(
+ exec_user["user_id"]
+ )
+ ):
+ time.sleep(3)
+ self.helper.websocket_helper.broadcast_user(
+ exec_user["user_id"],
+ "send_start_error",
+ {
+ "error": ""
+ " Not a server creator or server limit reached."
+ },
+ )
+ return
def is_name_used(name):
for server in self.controller.servers.get_all_defined_servers():
@@ -231,6 +249,7 @@ class ServerHandler(BaseHandler):
return True
return
+ template = "/panel/dashboard"
server_data = self.controller.servers.get_server_data_by_id(
server_id
)
@@ -265,6 +284,7 @@ class ServerHandler(BaseHandler):
backup_path = os.path.join(self.helper.backup_path, new_server_uuid)
server_port = server_data.get("server_port")
server_type = server_data.get("type")
+ created_by = exec_user["user_id"]
new_server_id = self.controller.servers.create_server(
new_server_name,
@@ -276,6 +296,7 @@ class ServerHandler(BaseHandler):
new_server_log_file,
stop_command,
server_type,
+ created_by,
server_port,
)
if not exec_user["superuser"]:
@@ -283,7 +304,8 @@ class ServerHandler(BaseHandler):
new_server_id
).get("server_uuid")
role_id = self.controller.roles.add_role(
- f"Creator of Server with uuid={new_server_uuid}"
+ f"Creator of Server with uuid={new_server_uuid}",
+ exec_user["user_id"],
)
self.controller.server_perms.add_role_server(
new_server_id, role_id, "11111111"
@@ -291,9 +313,6 @@ class ServerHandler(BaseHandler):
self.controller.users.add_role_to_user(
exec_user["user_id"], role_id
)
- self.controller.crafty_perms.add_server_creation(
- exec_user["user_id"]
- )
self.controller.servers.init_all_servers()
@@ -353,6 +372,7 @@ class ServerHandler(BaseHandler):
min_mem,
max_mem,
port,
+ exec_user["user_id"],
)
self.controller.management.add_to_audit_log(
exec_user["user_id"],
@@ -369,7 +389,13 @@ class ServerHandler(BaseHandler):
return
new_server_id = self.controller.import_zip_server(
- server_name, zip_path, import_server_jar, min_mem, max_mem, port
+ server_name,
+ zip_path,
+ import_server_jar,
+ min_mem,
+ max_mem,
+ port,
+ exec_user["user_id"],
)
if new_server_id == "false":
self.redirect(
@@ -385,8 +411,6 @@ class ServerHandler(BaseHandler):
new_server_id,
self.get_remote_ip(),
)
- # deletes temp dir
- FileHelpers.del_dirs(zip_path)
else:
if len(server_parts) != 3:
self.redirect("/panel/error?error=Invalid server data")
@@ -402,6 +426,7 @@ class ServerHandler(BaseHandler):
min_mem,
max_mem,
port,
+ exec_user["user_id"],
)
self.controller.management.add_to_audit_log(
exec_user["user_id"],
@@ -420,7 +445,8 @@ class ServerHandler(BaseHandler):
new_server_id
).get("server_uuid")
role_id = self.controller.roles.add_role(
- f"Creator of Server with uuid={new_server_uuid}"
+ f"Creator of Server with uuid={new_server_uuid}",
+ exec_user["user_id"],
)
self.controller.server_perms.add_role_server(
new_server_id, role_id, "11111111"
@@ -428,9 +454,6 @@ class ServerHandler(BaseHandler):
self.controller.users.add_role_to_user(
exec_user["user_id"], role_id
)
- self.controller.crafty_perms.add_server_creation(
- exec_user["user_id"]
- )
else:
for role in captured_roles:
@@ -483,7 +506,11 @@ class ServerHandler(BaseHandler):
return
new_server_id = self.controller.import_bedrock_server(
- server_name, import_server_path, import_server_exe, port
+ server_name,
+ import_server_path,
+ import_server_exe,
+ port,
+ exec_user["user_id"],
)
self.controller.management.add_to_audit_log(
exec_user["user_id"],
@@ -500,7 +527,11 @@ class ServerHandler(BaseHandler):
return
new_server_id = self.controller.import_bedrock_zip_server(
- server_name, zip_path, import_server_exe, port
+ server_name,
+ zip_path,
+ import_server_exe,
+ port,
+ exec_user["user_id"],
)
if new_server_id == "false":
self.redirect(
@@ -516,8 +547,6 @@ class ServerHandler(BaseHandler):
new_server_id,
self.get_remote_ip(),
)
- # deletes temp dir
- FileHelpers.del_dirs(zip_path)
else:
if len(server_parts) != 2:
self.redirect("/panel/error?error=Invalid server data")
@@ -526,7 +555,13 @@ class ServerHandler(BaseHandler):
# TODO: add server type check here and call the correct server
# add functions if not a jar
new_server_id = self.controller.create_jar_server(
- server_type, server_version, server_name, min_mem, max_mem, port
+ server_type,
+ server_version,
+ server_name,
+ min_mem,
+ max_mem,
+ port,
+ exec_user["user_id"],
)
self.controller.management.add_to_audit_log(
exec_user["user_id"],
@@ -545,7 +580,8 @@ class ServerHandler(BaseHandler):
new_server_id
).get("server_uuid")
role_id = self.controller.roles.add_role(
- f"Creator of Server with uuid={new_server_uuid}"
+ f"Creator of Server with uuid={new_server_uuid}",
+ exec_user["user_id"],
)
self.controller.server_perms.add_role_server(
new_server_id, role_id, "11111111"
@@ -553,9 +589,6 @@ class ServerHandler(BaseHandler):
self.controller.users.add_role_to_user(
exec_user["user_id"], role_id
)
- self.controller.crafty_perms.add_server_creation(
- exec_user["user_id"]
- )
else:
for role in captured_roles:
diff --git a/app/frontend/templates/panel/dashboard.html b/app/frontend/templates/panel/dashboard.html
index ec5a21db..23ebf2c7 100644
--- a/app/frontend/templates/panel/dashboard.html
+++ b/app/frontend/templates/panel/dashboard.html
@@ -193,9 +193,9 @@
{{ translate('dashboard', 'starting',
data['lang']) }}
- {% elif server['stats']['downloading']%}
+ {% elif server['stats']['importing']%}
- {{ translate('serverTerm', 'downloading',
+ {{ translate('serverTerm', 'importing',
data['lang']) }}
{% else %}
- {% elif server['stats']['downloading']%}
+ {% elif server['stats']['importing']%}