Merge branch 'dev' into feature/pretzel-metrics-graph

This commit is contained in:
Zedifus 2022-08-27 18:00:36 +01:00
commit fdb98cf1f8
38 changed files with 826 additions and 256 deletions

View File

@ -1,11 +1,14 @@
# Changelog # Changelog
## --- [4.0.11] - 2022/TBD ## --- [4.0.11] - 2022/TBD
### New features ### 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 ### Bug fixes
TBD - Fix creation quota not refilling after server delete ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/434))
### Tweaks ### 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 ### Lang
TBD TBD
<br><br> <br><br>

View File

@ -60,26 +60,6 @@ class CraftyPermsController:
permissions_list = PermissionsCrafty.get_permissions(permissions_mask) permissions_list = PermissionsCrafty.get_permissions(permissions_mask)
return permissions_list 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 @staticmethod
def get_api_key_permissions_list(key: ApiKeys): def get_api_key_permissions_list(key: ApiKeys):
return PermissionsCrafty.get_api_key_permissions_list(key) return PermissionsCrafty.get_api_key_permissions_list(key)

View File

@ -64,8 +64,8 @@ class RolesController:
HelperRoles.update_role(role_id, up_data) HelperRoles.update_role(role_id, up_data)
@staticmethod @staticmethod
def add_role(role_name): def add_role(role_name, manager):
return HelperRoles.add_role(role_name) return HelperRoles.add_role(role_name, manager)
class RoleServerJsonType(t.TypedDict): class RoleServerJsonType(t.TypedDict):
server_id: t.Union[str, int] server_id: t.Union[str, int]
@ -92,6 +92,7 @@ class RolesController:
def add_role_advanced( def add_role_advanced(
name: str, name: str,
servers: t.Iterable[RoleServerJsonType], servers: t.Iterable[RoleServerJsonType],
manager: int,
) -> int: ) -> int:
"""Add a role with a name and a list of servers """Add a role with a name and a list of servers
@ -102,7 +103,7 @@ class RolesController:
Returns: Returns:
int: The new role's ID 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: for server in servers:
PermissionsServers.get_or_create( PermissionsServers.get_or_create(
role_id, server["server_id"], server["permissions"] role_id, server["server_id"], server["permissions"]
@ -114,6 +115,7 @@ class RolesController:
role_id: t.Union[str, int], role_id: t.Union[str, int],
role_name: t.Optional[str], role_name: t.Optional[str],
servers: t.Optional[t.Iterable[RoleServerJsonType]], servers: t.Optional[t.Iterable[RoleServerJsonType]],
manager: int,
) -> None: ) -> None:
"""Update a role with a name and a list of servers """Update a role with a name and a list of servers
@ -152,6 +154,7 @@ class RolesController:
up_data = { up_data = {
"role_name": role_name, "role_name": role_name,
"last_update": Helpers.get_time_as_string(), "last_update": Helpers.get_time_as_string(),
"manager": manager,
} }
# TODO: do the last_update on the db side # TODO: do the last_update on the db side
HelperRoles.update_role(role_id, up_data) HelperRoles.update_role(role_id, up_data)

View File

@ -52,6 +52,7 @@ class ServersController(metaclass=Singleton):
server_log_file: str, server_log_file: str,
server_stop: str, server_stop: str,
server_type: str, server_type: str,
created_by: int,
server_port: int = 25565, server_port: int = 25565,
server_host: str = "127.0.0.1", server_host: str = "127.0.0.1",
) -> int: ) -> int:
@ -86,6 +87,7 @@ class ServersController(metaclass=Singleton):
server_log_file, server_log_file,
server_stop, server_stop,
server_type, server_type,
created_by,
server_port, server_port,
server_host, server_host,
) )
@ -116,19 +118,19 @@ class ServersController(metaclass=Singleton):
return ret return ret
@staticmethod @staticmethod
def set_download(server_id): def set_import(server_id):
srv = ServersController().get_server_instance_by_id(server_id) srv = ServersController().get_server_instance_by_id(server_id)
return srv.stats_helper.set_download() return srv.stats_helper.set_import()
@staticmethod @staticmethod
def finish_download(server_id): def finish_import(server_id):
srv = ServersController().get_server_instance_by_id(server_id) srv = ServersController().get_server_instance_by_id(server_id)
return srv.stats_helper.finish_download() return srv.stats_helper.finish_import()
@staticmethod @staticmethod
def get_download_status(server_id): def get_import_status(server_id):
server = ServersController().get_server_instance_by_id(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): def remove_server(self, server_id):
roles_list = PermissionsServers.get_roles_from_server(server_id) roles_list = PermissionsServers.get_roles_from_server(server_id)

View File

@ -1,7 +1,9 @@
import logging import logging
import typing as t import typing as t
from app.classes.models.servers import HelperServers
from app.classes.models.users import HelperUsers from app.classes.models.users import HelperUsers
from app.classes.models.roles import HelperRoles
from app.classes.models.crafty_permissions import ( from app.classes.models.crafty_permissions import (
PermissionsCrafty, PermissionsCrafty,
EnumPermissionsCrafty, EnumPermissionsCrafty,
@ -132,6 +134,18 @@ class UsersController:
def set_support_path(user_id, support_path): def set_support_path(user_id, support_path):
HelperUsers.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): def update_user(self, user_id: str, user_data=None, user_crafty_data=None):
if user_crafty_data is None: if user_crafty_data is None:
user_crafty_data = {} user_crafty_data = {}
@ -206,6 +220,7 @@ class UsersController:
def add_user( def add_user(
self, self,
username, username,
manager,
password, password,
email="default@example.com", email="default@example.com",
enabled: bool = True, enabled: bool = True,
@ -213,6 +228,7 @@ class UsersController:
): ):
return self.users_helper.add_user( return self.users_helper.add_user(
username, username,
manager,
password=password, password=password,
email=email, email=email,
enabled=enabled, enabled=enabled,
@ -236,6 +252,10 @@ class UsersController:
) )
def remove_user(self, user_id): 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) return self.users_helper.remove_user(user_id)
@staticmethod @staticmethod

View File

@ -175,7 +175,7 @@ class ServerJars:
# we submit a db update for it's stats. # we submit a db update for it's stats.
while True: while True:
try: try:
ServersController.set_download(server_id) ServersController.set_import(server_id)
for user in server_users: for user in server_users:
self.helper.websocket_helper.broadcast_user( self.helper.websocket_helper.broadcast_user(
user, "send_start_reload", {} user, "send_start_reload", {}
@ -190,7 +190,7 @@ class ServerJars:
try: try:
with open(path, "wb") as output: with open(path, "wb") as output:
shutil.copyfileobj(r.raw, output) shutil.copyfileobj(r.raw, output)
ServersController.finish_download(server_id) ServersController.finish_import(server_id)
for user in server_users: for user in server_users:
self.helper.websocket_helper.broadcast_user( self.helper.websocket_helper.broadcast_user(
@ -203,7 +203,7 @@ class ServerJars:
return True return True
except Exception as e: except Exception as e:
logger.error(f"Unable to save jar to {path} due to error:{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) server_users = PermissionsServers.get_server_user_list(server_id)
for user in server_users: for user in server_users:
self.helper.websocket_helper.broadcast_user( self.helper.websocket_helper.broadcast_user(

View File

@ -9,6 +9,7 @@ from peewee import (
) )
from app.classes.models.base_model import BaseModel 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.models.users import Users, ApiKeys, HelperUsers
from app.classes.shared.permission_helper import PermissionHelper from app.classes.shared.permission_helper import PermissionHelper
@ -23,9 +24,6 @@ class UserCrafty(BaseModel):
limit_server_creation = IntegerField(default=-1) limit_server_creation = IntegerField(default=-1)
limit_user_creation = IntegerField(default=0) limit_user_creation = IntegerField(default=0)
limit_role_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: class Meta:
table_name = "user_crafty" table_name = "user_crafty"
@ -107,9 +105,6 @@ class PermissionsCrafty:
UserCrafty.limit_server_creation: 0, UserCrafty.limit_server_creation: 0,
UserCrafty.limit_user_creation: 0, UserCrafty.limit_user_creation: 0,
UserCrafty.limit_role_creation: 0, UserCrafty.limit_role_creation: 0,
UserCrafty.created_server: 0,
UserCrafty.created_user: 0,
UserCrafty.created_role: 0,
} }
).execute() ).execute()
user_crafty = PermissionsCrafty.get_user_crafty(user_id) user_crafty = PermissionsCrafty.get_user_crafty(user_id)
@ -159,11 +154,16 @@ class PermissionsCrafty:
@staticmethod @staticmethod
def get_created_quantity_list(user_id): def get_created_quantity_list(user_id):
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
quantity_list = { quantity_list = {
EnumPermissionsCrafty.SERVER_CREATION.name: user_crafty.created_server, EnumPermissionsCrafty.SERVER_CREATION.name: HelperServers.get_total_owned_servers( # pylint: disable=line-too-long
EnumPermissionsCrafty.USER_CONFIG.name: user_crafty.created_user, user_id
EnumPermissionsCrafty.ROLES_CONFIG.name: user_crafty.created_role, ),
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 return quantity_list
@ -183,31 +183,6 @@ class PermissionsCrafty:
or limit_list[permission.name] == -1 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 @staticmethod
def get_api_key_permissions_list(key: ApiKeys): def get_api_key_permissions_list(key: ApiKeys):
user = HelperUsers.get_user(key.user_id) user = HelperUsers.get_user(key.user_id)

View File

@ -6,6 +6,7 @@ from peewee import (
DoesNotExist, DoesNotExist,
AutoField, AutoField,
DateTimeField, DateTimeField,
IntegerField,
) )
from playhouse.shortcuts import model_to_dict from playhouse.shortcuts import model_to_dict
@ -22,6 +23,7 @@ class Roles(BaseModel):
created = DateTimeField(default=datetime.datetime.now) created = DateTimeField(default=datetime.datetime.now)
last_update = DateTimeField(default=datetime.datetime.now) last_update = DateTimeField(default=datetime.datetime.now)
role_name = CharField(default="", unique=True, index=True) role_name = CharField(default="", unique=True, index=True)
manager = IntegerField(null=True)
class Meta: class Meta:
table_name = "roles" table_name = "roles"
@ -71,11 +73,12 @@ class HelperRoles:
) )
@staticmethod @staticmethod
def add_role(role_name): def add_role(role_name, manager):
role_id = Roles.insert( role_id = Roles.insert(
{ {
Roles.role_name: role_name.lower(), Roles.role_name: role_name.lower(),
Roles.created: Helpers.get_time_as_string(), Roles.created: Helpers.get_time_as_string(),
Roles.manager: manager,
} }
).execute() ).execute()
return role_id return role_id

View File

@ -53,7 +53,7 @@ class ServerStats(Model):
waiting_start = BooleanField(default=False) waiting_start = BooleanField(default=False)
first_run = BooleanField(default=True) first_run = BooleanField(default=True)
crashed = BooleanField(default=False) crashed = BooleanField(default=False)
downloading = BooleanField(default=False) importing = BooleanField(default=False)
class Meta: class Meta:
table_name = "server_stats" table_name = "server_stats"
@ -215,26 +215,26 @@ class HelperServerStats:
ServerStats.server_id == self.server_id ServerStats.server_id == self.server_id
).execute(self.database) ).execute(self.database)
def set_download(self): def set_import(self):
# self.select_database(self.server_id) # self.select_database(self.server_id)
ServerStats.update(downloading=True).where( ServerStats.update(importing=True).where(
ServerStats.server_id == self.server_id ServerStats.server_id == self.server_id
).execute(self.database) ).execute(self.database)
def finish_download(self): def finish_import(self):
# self.select_database(self.server_id) # self.select_database(self.server_id)
ServerStats.update(downloading=False).where( ServerStats.update(importing=False).where(
ServerStats.server_id == self.server_id ServerStats.server_id == self.server_id
).execute(self.database) ).execute(self.database)
def get_download_status(self): def get_import_status(self):
# self.select_database(self.server_id) # self.select_database(self.server_id)
download_status = ( import_status = (
ServerStats.select() ServerStats.select()
.where(ServerStats.server_id == self.server_id) .where(ServerStats.server_id == self.server_id)
.get(self.database) .get(self.database)
) )
return download_status.downloading return import_status.importing
def server_crash_reset(self): def server_crash_reset(self):
if self.server_id is None: if self.server_id is None:
@ -257,7 +257,6 @@ class HelperServerStats:
def set_update(self, value): def set_update(self, value):
if self.server_id is None: if self.server_id is None:
return return
# self.select_database(self.server_id) # self.select_database(self.server_id)
try: try:
# Checks if server even exists # Checks if server even exists

View File

@ -38,6 +38,8 @@ class Servers(BaseModel):
logs_delete_after = IntegerField(default=0) logs_delete_after = IntegerField(default=0)
type = CharField(default="minecraft-java") type = CharField(default="minecraft-java")
show_status = BooleanField(default=1) show_status = BooleanField(default=1)
created_by = IntegerField(default=-100)
shutdown_timeout = IntegerField(default=60)
class Meta: class Meta:
table_name = "servers" table_name = "servers"
@ -64,6 +66,7 @@ class HelperServers:
server_log_file: str, server_log_file: str,
server_stop: str, server_stop: str,
server_type: str, server_type: str,
created_by: int,
server_port: int = 25565, server_port: int = 25565,
server_host: str = "127.0.0.1", server_host: str = "127.0.0.1",
) -> int: ) -> int:
@ -105,6 +108,7 @@ class HelperServers:
Servers.stop_command: server_stop, Servers.stop_command: server_stop,
Servers.backup_path: backup_path, Servers.backup_path: backup_path,
Servers.type: server_type, Servers.type: server_type,
Servers.created_by: created_by,
} }
).execute() ).execute()
@ -112,6 +116,10 @@ class HelperServers:
def get_server_obj(server_id): def get_server_obj(server_id):
return Servers.get_by_id(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 @staticmethod
def get_server_type_by_id(server_id): def get_server_type_by_id(server_id):
server_type = Servers.select().where(Servers.server_id == server_id).get() server_type = Servers.select().where(Servers.server_id == server_id).get()

View File

@ -6,6 +6,7 @@ from peewee import (
ForeignKeyField, ForeignKeyField,
CharField, CharField,
AutoField, AutoField,
IntegerField,
DateTimeField, DateTimeField,
BooleanField, BooleanField,
CompositeKey, CompositeKey,
@ -40,6 +41,7 @@ class Users(BaseModel):
server_order = CharField(default="") server_order = CharField(default="")
preparing = BooleanField(default=False) preparing = BooleanField(default=False)
hints = BooleanField(default=True) hints = BooleanField(default=True)
manager = IntegerField(default=None, null=True)
class Meta: class Meta:
table_name = "users" table_name = "users"
@ -138,6 +140,16 @@ class HelperUsers:
user_query = Users.select().where(Users.user_id == user_id) user_query = Users.select().where(Users.user_id == user_id)
return user_query 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 @staticmethod
def get_user(user_id): def get_user(user_id):
if user_id == 0: if user_id == 0:
@ -192,6 +204,7 @@ class HelperUsers:
def add_user( def add_user(
self, self,
username: str, username: str,
manager: str,
password: str = None, password: str = None,
email: t.Optional[str] = None, email: t.Optional[str] = None,
enabled: bool = True, enabled: bool = True,
@ -209,6 +222,7 @@ class HelperUsers:
Users.enabled: enabled, Users.enabled: enabled,
Users.superuser: superuser, Users.superuser: superuser,
Users.created: Helpers.get_time_as_string(), Users.created: Helpers.get_time_as_string(),
Users.manager: manager,
} }
).execute() ).execute()
return user_id return user_id
@ -229,6 +243,7 @@ class HelperUsers:
Users.enabled: enabled, Users.enabled: enabled,
Users.superuser: superuser, Users.superuser: superuser,
Users.created: Helpers.get_time_as_string(), Users.created: Helpers.get_time_as_string(),
Users.manager: None,
} }
).execute() ).execute()
return user_id return user_id

View File

@ -27,10 +27,15 @@ class FileHelpers:
FileHelpers.del_dirs(sub) FileHelpers.del_dirs(sub)
else: else:
# Delete file if it is a file: # Delete file if it is a file:
try:
sub.unlink() sub.unlink()
except:
logger.error(f"Unable to delete file {sub}")
try:
# This removes the top-level folder: # This removes the top-level folder:
path.rmdir() path.rmdir()
except:
logger.error("Unable to remove top level")
return True return True
@staticmethod @staticmethod

View File

@ -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)

View File

@ -28,15 +28,17 @@ from app.classes.shared.authentication import Authentication
from app.classes.shared.console import Console from app.classes.shared.console import Console
from app.classes.shared.helpers import Helpers from app.classes.shared.helpers import Helpers
from app.classes.shared.file_helpers import FileHelpers from app.classes.shared.file_helpers import FileHelpers
from app.classes.shared.import_helper import ImportHelpers
from app.classes.minecraft.serverjars import ServerJars from app.classes.minecraft.serverjars import ServerJars
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Controller: class Controller:
def __init__(self, database, helper, file_helper): def __init__(self, database, helper, file_helper, import_helper):
self.helper: Helpers = helper self.helper: Helpers = helper
self.file_helper: FileHelpers = file_helper self.file_helper: FileHelpers = file_helper
self.import_helper: ImportHelpers = import_helper
self.server_jars: ServerJars = ServerJars(helper) self.server_jars: ServerJars = ServerJars(helper)
self.users_helper: HelperUsers = HelperUsers(database, self.helper) self.users_helper: HelperUsers = HelperUsers(database, self.helper)
self.roles_helper: HelperRoles = HelperRoles(database) self.roles_helper: HelperRoles = HelperRoles(database)
@ -244,7 +246,7 @@ class Controller:
except: except:
return {"percent": 0, "total_files": 0} 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() server_fs_uuid = Helpers.create_uuid()
new_server_path = os.path.join(self.helper.servers_dir, server_fs_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) 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 # TODO: Copy files from the zip file to the new server directory
server_file = create_data["jarfile"] server_file = create_data["jarfile"]
raise Exception("Not yet implemented") 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"] min_mem = create_data["mem_min"]
max_mem = create_data["mem_max"] max_mem = create_data["mem_max"]
@ -403,6 +407,7 @@ class Controller:
server_log_file=log_location, server_log_file=log_location,
server_stop=stop_command, server_stop=stop_command,
server_port=monitoring_port, server_port=monitoring_port,
created_by=user_id,
server_host=monitoring_host, server_host=monitoring_host,
server_type=monitoring_type, server_type=monitoring_type,
) )
@ -429,6 +434,7 @@ class Controller:
min_mem: int, min_mem: int,
max_mem: int, max_mem: int,
port: int, port: int,
user_id: int,
): ):
server_id = Helpers.create_uuid() server_id = Helpers.create_uuid()
server_dir = os.path.join(self.helper.servers_dir, server_id) server_dir = os.path.join(self.helper.servers_dir, server_id)
@ -489,6 +495,7 @@ class Controller:
server_log_file, server_log_file,
server_stop, server_stop,
port, port,
user_id,
server_type="minecraft-java", server_type="minecraft-java",
) )
@ -524,6 +531,7 @@ class Controller:
min_mem: int, min_mem: int,
max_mem: int, max_mem: int,
port: int, port: int,
user_id: int,
): ):
server_id = Helpers.create_uuid() server_id = Helpers.create_uuid()
new_server_dir = os.path.join(self.helper.servers_dir, server_id) 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(new_server_dir)
Helpers.ensure_dir_exists(backup_path) Helpers.ensure_dir_exists(backup_path)
server_path = Helpers.get_os_understandable_path(server_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) full_jar_path = os.path.join(new_server_dir, server_jar)
@ -584,8 +573,11 @@ class Controller:
server_log_file, server_log_file,
server_stop, server_stop,
port, port,
user_id,
server_type="minecraft-java", 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 return new_id
def import_zip_server( def import_zip_server(
@ -596,6 +588,7 @@ class Controller:
min_mem: int, min_mem: int,
max_mem: int, max_mem: int,
port: int, port: int,
user_id: int,
): ):
server_id = Helpers.create_uuid() server_id = Helpers.create_uuid()
new_server_dir = os.path.join(self.helper.servers_dir, server_id) 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) temp_dir = Helpers.get_os_understandable_path(zip_path)
Helpers.ensure_dir_exists(new_server_dir) Helpers.ensure_dir_exists(new_server_dir)
Helpers.ensure_dir_exists(backup_path) 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) full_jar_path = os.path.join(new_server_dir, server_jar)
@ -664,8 +631,13 @@ class Controller:
server_log_file, server_log_file,
server_stop, server_stop,
port, port,
user_id,
server_type="minecraft-java", 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 return new_id
# ********************************************************************************** # **********************************************************************************
@ -673,7 +645,12 @@ class Controller:
# ********************************************************************************** # **********************************************************************************
def import_bedrock_server( 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() server_id = Helpers.create_uuid()
new_server_dir = os.path.join(self.helper.servers_dir, server_id) 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(new_server_dir)
Helpers.ensure_dir_exists(backup_path) Helpers.ensure_dir_exists(backup_path)
server_path = Helpers.get_os_understandable_path(server_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) full_jar_path = os.path.join(new_server_dir, server_exe)
@ -727,15 +685,22 @@ class Controller:
server_log_file, server_log_file,
server_stop, server_stop,
port, port,
user_id,
server_type="minecraft-bedrock", server_type="minecraft-bedrock",
) )
if os.name != "nt": ServersController.set_import(new_id)
if Helpers.check_file_exists(full_jar_path): self.import_helper.import_bedrock_server(
os.chmod(full_jar_path, 0o2760) server_path, new_server_dir, port, full_jar_path, new_id
)
return new_id return new_id
def import_bedrock_zip_server( 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() server_id = Helpers.create_uuid()
new_server_dir = os.path.join(self.helper.servers_dir, server_id) 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) temp_dir = Helpers.get_os_understandable_path(zip_path)
Helpers.ensure_dir_exists(new_server_dir) Helpers.ensure_dir_exists(new_server_dir)
Helpers.ensure_dir_exists(backup_path) 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) full_jar_path = os.path.join(new_server_dir, server_exe)
@ -796,8 +735,12 @@ class Controller:
server_log_file, server_log_file,
server_stop, server_stop,
port, port,
user_id,
server_type="minecraft-bedrock", 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 os.name != "nt":
if Helpers.check_file_exists(full_jar_path): if Helpers.check_file_exists(full_jar_path):
os.chmod(full_jar_path, 0o2760) os.chmod(full_jar_path, 0o2760)
@ -838,6 +781,7 @@ class Controller:
server_log_file: str, server_log_file: str,
server_stop: str, server_stop: str,
server_port: int, server_port: int,
created_by: int,
server_type: str, server_type: str,
server_host: str = "127.0.0.1", server_host: str = "127.0.0.1",
): ):
@ -852,6 +796,7 @@ class Controller:
server_log_file, server_log_file,
server_stop, server_stop,
server_type, server_type,
created_by,
server_port, server_port,
server_host, server_host,
) )

View File

@ -26,6 +26,7 @@ class DatabaseBuilder:
password=password, password=password,
email="default@example.com", email="default@example.com",
superuser=True, superuser=True,
manager=None,
) )
def is_fresh_install(self): def is_fresh_install(self):

View File

@ -293,7 +293,7 @@ class ServerInstance:
else: else:
user_lang = HelperUsers.get_user_lang_by_id(user_id) 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: if user_id:
self.helper.websocket_helper.broadcast_user( self.helper.websocket_helper.broadcast_user(
user_id, user_id,
@ -612,12 +612,16 @@ class ServerInstance:
# caching the name and pid number # caching the name and pid number
server_name = self.name server_name = self.name
server_pid = self.process.pid server_pid = self.process.pid
self.shutdown_timeout = self.settings["shutdown_timeout"]
while running: while running:
i += 1 i += 1
ttk = int(self.shutdown_timeout - (i * 2))
if i <= self.shutdown_timeout / 2:
logstr = ( logstr = (
f"Server {server_name} is still running " f"Server {server_name} is still running "
f"- waiting 2s to see if it stops ({int(60-(i*2))} " "- waiting 2s to see if it stops"
f"({ttk} "
f"seconds until force close)" f"seconds until force close)"
) )
logger.info(logstr) logger.info(logstr)
@ -626,7 +630,7 @@ class ServerInstance:
time.sleep(2) time.sleep(2)
# if we haven't closed in 60 seconds, let's just slam down on the PID # 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( logger.info(
f"Server {server_name} is still running - Forcing the process down" f"Server {server_name} is still running - Forcing the process down"
) )

View File

@ -394,6 +394,7 @@ class AjaxHandler(BaseHandler):
"1", "1",
"2", "2",
server_data["server_port"], server_data["server_port"],
server_data["created_by"],
) )
new_server_id = new_server new_server_id = new_server
new_server = self.controller.servers.get_server_data(new_server) new_server = self.controller.servers.get_server_data(new_server)
@ -416,6 +417,7 @@ class AjaxHandler(BaseHandler):
temp_dir, temp_dir,
server_data["executable"], server_data["executable"],
server_data["server_port"], server_data["server_port"],
server_data["created_by"],
) )
new_server_id = new_server new_server_id = new_server
new_server = self.controller.servers.get_server_data(new_server) new_server = self.controller.servers.get_server_data(new_server)

View File

@ -340,10 +340,11 @@ class CreateUser(ApiHandler):
new_username = self.get_argument("username").lower() new_username = self.get_argument("username").lower()
new_pass = self.get_argument("password") new_pass = self.get_argument("password")
manager = int(user_obj["user_id"])
if new_username: if new_username:
self.controller.users.add_user( 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( self.return_response(

View File

@ -453,8 +453,8 @@ class PanelHandler(BaseHandler):
for server in un_used_servers[:]: for server in un_used_servers[:]:
if flag == 0: if flag == 0:
server["stats"][ server["stats"][
"downloading" "importing"
] = self.controller.servers.get_download_status( ] = self.controller.servers.get_import_status(
str(server["stats"]["server_id"]["server_id"]) str(server["stats"]["server_id"]["server_id"])
) )
server["stats"]["crashed"] = self.controller.servers.is_crashed( server["stats"]["crashed"] = self.controller.servers.is_crashed(
@ -572,11 +572,11 @@ class PanelHandler(BaseHandler):
"started": "False", "started": "False",
} }
if not self.failed_server: 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 server_id
) )
else: else:
page_data["downloading"] = False page_data["importing"] = False
page_data["server_id"] = server_id page_data["server_id"] = server_id
try: try:
page_data["waiting_start"] = self.controller.servers.get_waiting_start( 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["users"] = self.controller.users.get_all_users()
page_data["roles"] = self.controller.roles.get_all_roles() page_data["roles"] = self.controller.roles.get_all_roles()
page_data["auth-servers"][user.user_id] = super_auth_servers 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" template = "panel/panel_config.html"
@ -891,7 +903,7 @@ class PanelHandler(BaseHandler):
) )
return 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"] = []
page_data["servers_all"] = self.controller.servers.get_all_defined_servers() page_data["servers_all"] = self.controller.servers.get_all_defined_servers()
page_data["role-servers"] = [] page_data["role-servers"] = []
@ -910,8 +922,16 @@ class PanelHandler(BaseHandler):
) )
if superuser: if superuser:
page_data["super-disabled"] = "" page_data["super-disabled"] = ""
page_data["users"] = self.controller.users.get_all_users()
else: else:
page_data["super-disabled"] = "disabled" 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( for file in sorted(
os.listdir(os.path.join(self.helper.root_dir, "app", "translations")) 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["user"] = self.controller.users.get_user_by_id(user_id)
page_data["servers"] = set() page_data["servers"] = set()
page_data["role-servers"] = page_role_servers 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["servers_all"] = self.controller.servers.get_all_defined_servers()
page_data["superuser"] = superuser 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[ page_data[
"permissions_all" "permissions_all"
] = self.controller.crafty_perms.list_defined_crafty_permissions() ] = self.controller.crafty_perms.list_defined_crafty_permissions()
@ -1121,6 +1153,17 @@ class PanelHandler(BaseHandler):
"/panel/error?error=Unauthorized access: not a user editor" "/panel/error?error=Unauthorized access: not a user editor"
) )
return 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["servers"] = []
page_data["role-servers"] = [] page_data["role-servers"] = []
@ -1218,6 +1261,11 @@ class PanelHandler(BaseHandler):
defined_servers = self.controller.servers.get_authorized_servers( defined_servers = self.controller.servers.get_authorized_servers(
exec_user["user_id"] exec_user["user_id"]
) )
page_data["role_manager"] = {
"user_id": -100,
"username": "None",
}
page_servers = [] page_servers = []
for server in defined_servers: for server in defined_servers:
if server not in page_servers: if server not in page_servers:
@ -1235,6 +1283,7 @@ class PanelHandler(BaseHandler):
user_roles = self.get_user_roles() user_roles = self.get_user_roles()
page_data["new_role"] = False page_data["new_role"] = False
role_id = self.get_argument("id", None) 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) page_data["role"] = self.controller.roles.get_role_with_servers(role_id)
if exec_user["superuser"]: if exec_user["superuser"]:
defined_servers = self.controller.servers.list_defined_servers() defined_servers = self.controller.servers.list_defined_servers()
@ -1258,7 +1307,21 @@ class PanelHandler(BaseHandler):
page_data["user-roles"] = user_roles page_data["user-roles"] = user_roles
page_data["users"] = self.controller.users.get_all_users() 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( self.redirect(
"/panel/error?error=Unauthorized access: not a role editor" "/panel/error?error=Unauthorized access: not a role editor"
) )
@ -1272,8 +1335,15 @@ class PanelHandler(BaseHandler):
elif page == "remove_role": elif page == "remove_role":
role_id = bleach.clean(self.get_argument("id", None)) role_id = bleach.clean(self.get_argument("id", None))
if not superuser: if (
self.redirect("/panel/error?error=Unauthorized access: not superuser") 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 return
if role_id is None: if role_id is None:
self.redirect("/panel/error?error=Invalid Role ID") self.redirect("/panel/error?error=Invalid Role ID")
@ -1424,6 +1494,7 @@ class PanelHandler(BaseHandler):
return return
server_name = self.get_argument("server_name", None) server_name = self.get_argument("server_name", None)
server_obj = self.controller.servers.get_server_obj(server_id) server_obj = self.controller.servers.get_server_obj(server_id)
shutdown_timeout = self.get_argument("shutdown_timeout", 60)
if superuser: if superuser:
server_path = self.get_argument("server_path", None) server_path = self.get_argument("server_path", None)
if Helpers.is_os_windows(): if Helpers.is_os_windows():
@ -1504,6 +1575,7 @@ class PanelHandler(BaseHandler):
) )
server_obj.server_name = server_name server_obj.server_name = server_name
server_obj.shutdown_timeout = shutdown_timeout
if superuser: if superuser:
if Helpers.validate_traversal( if Helpers.validate_traversal(
self.helper.get_servers_root_dir(), server_path self.helper.get_servers_root_dir(), server_path
@ -1936,6 +2008,7 @@ class PanelHandler(BaseHandler):
"system user is not editable" "system user is not editable"
) )
user_id = bleach.clean(self.get_argument("id", None)) 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()) username = bleach.clean(self.get_argument("username", None).lower())
if ( if (
username != self.controller.users.get_user_by_id(user_id)["username"] username != self.controller.users.get_user_by_id(user_id)["username"]
@ -1968,7 +2041,19 @@ class PanelHandler(BaseHandler):
else: else:
superuser = 0 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 == "": if username is None or username == "":
self.redirect("/panel/error?error=Invalid username") self.redirect("/panel/error?error=Invalid username")
return return
@ -2019,6 +2104,7 @@ class PanelHandler(BaseHandler):
user_data = { user_data = {
"username": username, "username": username,
"manager": manager,
"password": password0, "password": password0,
"email": email, "email": email,
"enabled": enabled, "enabled": enabled,
@ -2164,6 +2250,15 @@ class PanelHandler(BaseHandler):
if username is None or username == "": if username is None or username == "":
self.redirect("/panel/error?error=Invalid username") self.redirect("/panel/error?error=Invalid username")
return 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? # does this user id exist?
if self.controller.users.get_id_by_name(username) is not None: if self.controller.users.get_id_by_name(username) is not None:
self.redirect("/panel/error?error=User exists") self.redirect("/panel/error?error=User exists")
@ -2178,6 +2273,7 @@ class PanelHandler(BaseHandler):
user_id = self.controller.users.add_user( user_id = self.controller.users.add_user(
username, username,
manager=manager,
password=password0, password=password0,
email=email, email=email,
enabled=enabled, enabled=enabled,
@ -2204,14 +2300,19 @@ class PanelHandler(BaseHandler):
server_id=0, server_id=0,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )
self.controller.crafty_perms.add_user_creation(exec_user["user_id"])
self.redirect("/panel/panel_config") self.redirect("/panel/panel_config")
elif page == "edit_role": elif page == "edit_role":
role_id = bleach.clean(self.get_argument("id", None)) role_id = bleach.clean(self.get_argument("id", None))
role_name = bleach.clean(self.get_argument("role_name", 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( self.redirect(
"/panel/error?error=Unauthorized access: not a role editor" "/panel/error?error=Unauthorized access: not a role editor"
) )
@ -2227,9 +2328,18 @@ class PanelHandler(BaseHandler):
self.redirect("/panel/error?error=Invalid Role ID") self.redirect("/panel/error?error=Invalid Role ID")
return return
if exec_user["superuser"]:
manager = self.get_argument("manager", None)
if manager == "":
manager = None
else:
manager = role["manager"]
servers = self.get_role_servers() 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( self.controller.management.add_to_audit_log(
exec_user["user_id"], exec_user["user_id"],
@ -2241,6 +2351,12 @@ class PanelHandler(BaseHandler):
elif page == "add_role": elif page == "add_role":
role_name = bleach.clean(self.get_argument("role_name", None)) role_name = bleach.clean(self.get_argument("role_name", None))
if 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: if EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions:
self.redirect( self.redirect(
@ -2265,7 +2381,9 @@ class PanelHandler(BaseHandler):
servers = self.get_role_servers() 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( self.controller.management.add_to_audit_log(
exec_user["user_id"], exec_user["user_id"],
@ -2273,7 +2391,6 @@ class PanelHandler(BaseHandler):
server_id=0, server_id=0,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )
self.controller.crafty_perms.add_role_creation(exec_user["user_id"])
self.redirect("/panel/panel_config") self.redirect("/panel/panel_config")
else: else:

View File

@ -116,7 +116,9 @@ class ApiRolesIndexHandler(BaseApiHandler):
400, {"status": "error", "error": "ROLE_NAME_ALREADY_EXISTS"} 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( self.controller.management.add_to_audit_log(
user["user_id"], user["user_id"],

View File

@ -665,10 +665,9 @@ class ApiServersIndexHandler(BaseApiHandler):
}, },
) )
new_server_id, new_server_uuid = self.controller.create_api_server(data) new_server_id, new_server_uuid = self.controller.create_api_server(
data, user["user_id"]
# Increase the server creation counter )
self.controller.crafty_perms.add_server_creation(user["user_id"])
self.controller.servers.stats.record_stats() self.controller.servers.stats.record_stats()

View File

@ -84,6 +84,7 @@ class ApiServersServerActionHandler(BaseApiHandler):
new_server_log_file, new_server_log_file,
server_data.get("stop_command"), server_data.get("stop_command"),
server_data.get("type"), server_data.get("type"),
user_id,
server_data.get("server_port"), server_data.get("server_port"),
) )

View File

@ -96,6 +96,7 @@ class ApiUsersIndexHandler(BaseApiHandler):
username = data["username"] username = data["username"]
username = str(username).lower() username = str(username).lower()
manager = int(user["user_id"])
password = data["password"] password = data["password"]
email = data.get("email", "default@example.com") email = data.get("email", "default@example.com")
enabled = data.get("enabled", True) enabled = data.get("enabled", True)
@ -149,6 +150,7 @@ class ApiUsersIndexHandler(BaseApiHandler):
# TODO: do this in the most efficient way # TODO: do this in the most efficient way
user_id = self.controller.users.add_user( user_id = self.controller.users.add_user(
username, username,
manager,
password, password,
email, email,
enabled, enabled,

View File

@ -1,6 +1,7 @@
import json import json
import logging import logging
import os import os
import time
import tornado.web import tornado.web
import tornado.escape import tornado.escape
import bleach import bleach
@ -224,6 +225,23 @@ class ServerHandler(BaseHandler):
if server_id is not None: if server_id is not None:
if command == "clone_server": 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": "<i class='fas fa-exclamation-triangle'"
" style='font-size:48px;color:red'>"
"</i> Not a server creator or server limit reached."
},
)
return
def is_name_used(name): def is_name_used(name):
for server in self.controller.servers.get_all_defined_servers(): for server in self.controller.servers.get_all_defined_servers():
@ -231,6 +249,7 @@ class ServerHandler(BaseHandler):
return True return True
return return
template = "/panel/dashboard"
server_data = self.controller.servers.get_server_data_by_id( server_data = self.controller.servers.get_server_data_by_id(
server_id server_id
) )
@ -265,6 +284,7 @@ class ServerHandler(BaseHandler):
backup_path = os.path.join(self.helper.backup_path, new_server_uuid) backup_path = os.path.join(self.helper.backup_path, new_server_uuid)
server_port = server_data.get("server_port") server_port = server_data.get("server_port")
server_type = server_data.get("type") server_type = server_data.get("type")
created_by = exec_user["user_id"]
new_server_id = self.controller.servers.create_server( new_server_id = self.controller.servers.create_server(
new_server_name, new_server_name,
@ -276,6 +296,7 @@ class ServerHandler(BaseHandler):
new_server_log_file, new_server_log_file,
stop_command, stop_command,
server_type, server_type,
created_by,
server_port, server_port,
) )
if not exec_user["superuser"]: if not exec_user["superuser"]:
@ -283,7 +304,8 @@ class ServerHandler(BaseHandler):
new_server_id new_server_id
).get("server_uuid") ).get("server_uuid")
role_id = self.controller.roles.add_role( 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( self.controller.server_perms.add_role_server(
new_server_id, role_id, "11111111" new_server_id, role_id, "11111111"
@ -291,9 +313,6 @@ class ServerHandler(BaseHandler):
self.controller.users.add_role_to_user( self.controller.users.add_role_to_user(
exec_user["user_id"], role_id exec_user["user_id"], role_id
) )
self.controller.crafty_perms.add_server_creation(
exec_user["user_id"]
)
self.controller.servers.init_all_servers() self.controller.servers.init_all_servers()
@ -353,6 +372,7 @@ class ServerHandler(BaseHandler):
min_mem, min_mem,
max_mem, max_mem,
port, port,
exec_user["user_id"],
) )
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
exec_user["user_id"], exec_user["user_id"],
@ -369,7 +389,13 @@ class ServerHandler(BaseHandler):
return return
new_server_id = self.controller.import_zip_server( 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": if new_server_id == "false":
self.redirect( self.redirect(
@ -385,8 +411,6 @@ class ServerHandler(BaseHandler):
new_server_id, new_server_id,
self.get_remote_ip(), self.get_remote_ip(),
) )
# deletes temp dir
FileHelpers.del_dirs(zip_path)
else: else:
if len(server_parts) != 3: if len(server_parts) != 3:
self.redirect("/panel/error?error=Invalid server data") self.redirect("/panel/error?error=Invalid server data")
@ -402,6 +426,7 @@ class ServerHandler(BaseHandler):
min_mem, min_mem,
max_mem, max_mem,
port, port,
exec_user["user_id"],
) )
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
exec_user["user_id"], exec_user["user_id"],
@ -420,7 +445,8 @@ class ServerHandler(BaseHandler):
new_server_id new_server_id
).get("server_uuid") ).get("server_uuid")
role_id = self.controller.roles.add_role( 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( self.controller.server_perms.add_role_server(
new_server_id, role_id, "11111111" new_server_id, role_id, "11111111"
@ -428,9 +454,6 @@ class ServerHandler(BaseHandler):
self.controller.users.add_role_to_user( self.controller.users.add_role_to_user(
exec_user["user_id"], role_id exec_user["user_id"], role_id
) )
self.controller.crafty_perms.add_server_creation(
exec_user["user_id"]
)
else: else:
for role in captured_roles: for role in captured_roles:
@ -483,7 +506,11 @@ class ServerHandler(BaseHandler):
return return
new_server_id = self.controller.import_bedrock_server( 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( self.controller.management.add_to_audit_log(
exec_user["user_id"], exec_user["user_id"],
@ -500,7 +527,11 @@ class ServerHandler(BaseHandler):
return return
new_server_id = self.controller.import_bedrock_zip_server( 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": if new_server_id == "false":
self.redirect( self.redirect(
@ -516,8 +547,6 @@ class ServerHandler(BaseHandler):
new_server_id, new_server_id,
self.get_remote_ip(), self.get_remote_ip(),
) )
# deletes temp dir
FileHelpers.del_dirs(zip_path)
else: else:
if len(server_parts) != 2: if len(server_parts) != 2:
self.redirect("/panel/error?error=Invalid server data") 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 # TODO: add server type check here and call the correct server
# add functions if not a jar # add functions if not a jar
new_server_id = self.controller.create_jar_server( 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( self.controller.management.add_to_audit_log(
exec_user["user_id"], exec_user["user_id"],
@ -545,7 +580,8 @@ class ServerHandler(BaseHandler):
new_server_id new_server_id
).get("server_uuid") ).get("server_uuid")
role_id = self.controller.roles.add_role( 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( self.controller.server_perms.add_role_server(
new_server_id, role_id, "11111111" new_server_id, role_id, "11111111"
@ -553,9 +589,6 @@ class ServerHandler(BaseHandler):
self.controller.users.add_role_to_user( self.controller.users.add_role_to_user(
exec_user["user_id"], role_id exec_user["user_id"], role_id
) )
self.controller.crafty_perms.add_server_creation(
exec_user["user_id"]
)
else: else:
for role in captured_roles: for role in captured_roles:

View File

@ -193,9 +193,9 @@
<a data-id="{{server['server_data']['server_id']}}" class="" title="{{ <a data-id="{{server['server_data']['server_id']}}" class="" title="{{
translate('dashboard', 'delay-explained' , data['lang'])}}">{{ translate('dashboard', 'starting', translate('dashboard', 'delay-explained' , data['lang'])}}">{{ translate('dashboard', 'starting',
data['lang']) }}</i></a> data['lang']) }}</i></a>
{% elif server['stats']['downloading']%} {% elif server['stats']['importing']%}
<a data-id="{{server['server_data']['server_id']}}" class=""><i class="fa fa-spinner fa-spin"></i> <a data-id="{{server['server_data']['server_id']}}" class=""><i class="fa fa-spinner fa-spin"></i>
{{ translate('serverTerm', 'downloading', {{ translate('serverTerm', 'importing',
data['lang']) }}</a> data['lang']) }}</a>
{% else %} {% else %}
<a data-id="{{server['server_data']['server_id']}}" class="play_button" data-toggle="tooltip" <a data-id="{{server['server_data']['server_id']}}" class="play_button" data-toggle="tooltip"
@ -399,12 +399,12 @@
data['lang']) }}</i></a> data['lang']) }}</i></a>
</div> </div>
</div> </div>
{% elif server['stats']['downloading']%} {% elif server['stats']['importing']%}
<div class="row"> <div class="row">
<div class="col-12 px-0"> <div class="col-12 px-0">
<a data-id="{{server['server_data']['server_id']}}" class="btn btn-link"><i <a data-id="{{server['server_data']['server_id']}}" class="btn btn-link"><i
class="fa fa-spinner fa-spin"></i> class="fa fa-spinner fa-spin"></i>
{{ translate('serverTerm', 'downloading', data['lang']) }}</a> {{ translate('serverTerm', 'importing', data['lang']) }}</a>
</div> </div>
</div> </div>
{% else %} {% else %}
@ -893,9 +893,6 @@
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; {% raw translate("dashboard", "bePatientClone", data["lang"]) %} </div>', message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; {% raw translate("dashboard", "bePatientClone", data["lang"]) %} </div>',
closeButton: false, closeButton: false,
}); });
setTimeout(function () {
location.reload();
}, 5000)
} }
</script> </script>
<script src="/static/assets/vendors/js/jquery-ui.js"></script> <script src="/static/assets/vendors/js/jquery-ui.js"></script>

View File

@ -87,6 +87,38 @@
<td><a href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-pencil-alt"></i></a></td> <td><a href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-pencil-alt"></i></a></td>
</tr> </tr>
{% end %} {% end %}
{% for user in data['managed_users'] %}
<tr>
<td><i class="fas fa-user"></i> {{ user.username }}</td>
<td>
{% if user.enabled %}
<span class="text-success">
<i class="fas fa-check-square"></i> Yes
</span>
{% else %}
<span class="text-danger">
<i class="far fa-times-square"></i> No
</span>
{% end %}
</td>
<td id="server_list_{{user.user_id}}">
<ul id="{{user.user_id}}">
{% for item in data['auth-servers'][user.user_id] %}
<li>{{item}}</li>
{% end %}
</ul>
</td>
<td id="role_list_{{user.user_id}}">
<ul>
{% for item in data['user-roles'][user.user_id] %}
<li data-toggle="tooltip" title="{{ item }}">{{item}}</li>
{% end %}
</ul>
</td>
<td><a href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-pencil-alt"></i></a></td>
</tr>
{% end %}
</tbody> </tbody>
</table> </table>
</div> </div>
@ -146,6 +178,34 @@
</tr> </tr>
{% end %} {% end %}
{% if not data['superuser'] %}
{% for role in data['managed_roles'] %}
{% if role.role_id not in data['assigned_roles'] %}
<tr>
<td>{{ role.role_name }}</td>
<td id="role_list_{{role.role_id}}">
<ul id="{{role.role_id}}">
{% for item in data['role-servers'][role.role_id] %}
<li>{{item}}</li>
{% end %}
</ul>
</td>
<td>
<ul>
{% for user in data['users'] %}
{% for ruser in data['user-roles'][user.user_id] %}
{% if ruser == role.role_name %}
<li>{{ user.username }}</li>
{% end %}
{% end %}
{% end %}
</ul>
</td>
<td><a href="/panel/edit_role?id={{role.role_id}}"><i class="fas fa-pencil-alt"></i></a></td>
</tr>
{% end %}
{% end %}
{% end %}
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -49,7 +49,7 @@
</ul> </ul>
<div class=""> <div class="">
<div class=""> <div class="">
<form class="forms-sample" method="post" action="{{ '/panel/add_role' if data['new_role'] else '/panel/edit_role' }}"> <form id="role_form" class="forms-sample" method="post" action="{{ '/panel/add_role' if data['new_role'] else '/panel/edit_role' }}">
{% raw xsrf_form_html() %} {% raw xsrf_form_html() %}
<input type="hidden" name="id" value="{{ data['role']['role_id'] }}"> <input type="hidden" name="id" value="{{ data['role']['role_id'] }}">
<input type="hidden" name="subpage" value="config"> <input type="hidden" name="subpage" value="config">
@ -63,6 +63,29 @@
<label for="role_name">{{ translate('rolesConfig', 'roleName', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('rolesConfig', 'roleDesc', data['lang']) }}</small> </label> <label for="role_name">{{ translate('rolesConfig', 'roleName', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('rolesConfig', 'roleDesc', data['lang']) }}</small> </label>
<input type="text" class="form-control" name="role_name" id="role_name" value="{{ data['role']['role_name'] }}" placeholder="Role Name" > <input type="text" class="form-control" name="role_name" id="role_name" value="{{ data['role']['role_name'] }}" placeholder="Role Name" >
</div> </div>
<br />
{% if data['superuser'] %}
<div class="form-group">
<label class="form-label" for="manager">{{ translate('rolesConfig', 'selectManager',
data['lang']) }}
</label>
<select class="form-select form-control form-control-lg select-css" id="manager" name="manager"
form="role_form">
{% if data["role_manager"]["username"] != "None" %}
<option value='{{data["role_manager"]["user_id"]}}'>{{ data["role_manager"]["username"]
}}</option>
{% end %}
<option value="">None</option>
{% for user in data['users'] %}
{% if user.user_id != data['role_manager']['user_id']
%}
<option value="{{user.user_id}}">{{user.username}}</option>
{% end %}
{% end %}
</select>
</div>
{% end %}
</div> </div>
</div> </div>
@ -252,6 +275,8 @@
<br /> <br />
{{ translate('rolesConfig', 'configUpdate', data['lang']) }} {{ str(data['role']['last_update']) }} {{ translate('rolesConfig', 'configUpdate', data['lang']) }} {{ str(data['role']['last_update']) }}
<br /> <br />
{{ translate('userConfig', 'manager', data['lang']) }}: {{ data['role_manager']['username'] }}
<br />
</p> </p>
</blockquote> </blockquote>
<div class="text-center"> <div class="text-center">

View File

@ -121,6 +121,27 @@ data['lang']) }}{% end %}
{% end %} {% end %}
</select> </select>
</div> </div>
{% if data['superuser'] %}
<div class="form-group">
<label class="form-label" for="manager">{{ translate('userConfig', 'selectManager',
data['lang']) }}
</label>
<select class="form-select form-control form-control-lg select-css" id="manager" name="manager"
form="user_form">
{% if data["manager"]["username"] != "None" %}
<option value='{{data["manager"]["user_id"]}}'>{{ data["manager"]["username"]
}}</option>
{% end %}
<option value="">None</option>
{% for user in data['users'] %}
{% if user.username != data['user']['username'] and user.user_id != data['manager']['user_id']
%}
<option value="{{user.user_id}}">{{user.username}}</option>
{% end %}
{% end %}
</select>
</div>
{% end %}
</div> </div>
</div> </div>
@ -141,21 +162,29 @@ data['lang']) }}{% end %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for role in data['roles_all'] %} {% for role in data['roles'] %}
<tr> <tr>
{% if data['superuser'] or role.role_id in data['user']['roles'] or role.manager == data['exec_user'] %}
<td>{{ role.role_name }}</td> <td>{{ role.role_name }}</td>
<td> <td>
{% if role.role_id in data['user']['roles'] %} {% if role.role_id in data['user']['roles'] %}
{% if role.manager == data['exec_user'] or data['superuser'] %}
<input type="checkbox" class="form-check-input" <input type="checkbox" class="form-check-input"
id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership" id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership"
checked="" value="1"> checked="" value="1">
{% else %} {% else %}
<input type="checkbox" class="form-check-input"
id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership"
checked="" value="1" disabled>
{% end %}
{% elif data['superuser'] or role.manager == data['exec_user'] %}
<input type="checkbox" class="form-check-input" <input type="checkbox" class="form-check-input"
id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership" id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership"
value="1"> value="1">
{% end %} {% end %}
</td> </td>
{% end %}
</tr> </tr>
{% end %} {% end %}
</tbody> </tbody>
@ -268,6 +297,8 @@ data['lang']) }}{% end %}
<br /> <br />
{{ translate('userConfig', 'lastIP', data['lang']) }} {{ data['user']['last_ip'] }} {{ translate('userConfig', 'lastIP', data['lang']) }} {{ data['user']['last_ip'] }}
<br /> <br />
{{ translate('userConfig', 'manager', data['lang'])}}: {{data['manager']['username'] }}
<br />
</p> </p>
</blockquote> </blockquote>
</div> </div>
@ -284,7 +315,7 @@ data['lang']) }}{% end %}
{% else %} {% else %}
<button class="btn btn-sm btn-danger delete-user"><i class="fas fa-trash"></i> {{ <button class="btn btn-sm btn-danger delete-user"><i class="fas fa-trash"></i> {{
translate('userConfig', 'deleteUserB', data['lang']) }}</a> translate('userConfig', 'deleteUserB', data['lang']) }}</a>
{% end %} {% end %}</button>
</div> </div>
</div> </div>

View File

@ -165,6 +165,18 @@
</div> </div>
{% end %} {% end %}
<div class="form-group">
<label for="shutdown_timeout">{{ translate('serverConfig', 'shutdownTimeout', data['lang']) }}
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'timeoutExplain1', data['lang'])
}}&nbsp;
{{ data['server_stats']['server_id']['stop_command'] }}&nbsp;{{ translate('serverConfig',
'timeoutExplain2', data['lang']) }}
</small> </label>
<input type="number" class="form-control" name="shutdown_timeout" id="shutdown_timeout"
value="{{ data['server_stats']['server_id']['shutdown_timeout'] }}" step="2" max="300" min="60"
required>
</div>
<div class="form-group"> <div class="form-group">
<label for="logs_delete_after">{{ translate('serverConfig', 'removeOldLogsAfter', data['lang']) }} <label for="logs_delete_after">{{ translate('serverConfig', 'removeOldLogsAfter', data['lang']) }}
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'removeOldLogsAfterDesc', <small class="text-muted ml-1"> - {{ translate('serverConfig', 'removeOldLogsAfterDesc',

View File

@ -65,9 +65,9 @@
<button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart', data['lang']) %}</button> <button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart', data['lang']) %}</button>
<button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button> <button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
</div> </div>
{% elif data['downloading'] %} {% elif data['importing'] %}
<div id="control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible"> <div id="control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible">
<button onclick="" id="start-btn" style="max-width: 12rem; white-space: nowrap;" class="btn btn-secondary m-1 flex-grow-1 disabled"><i class="fa fa-spinner fa-spin"></i> {{ translate('serverTerm', 'downloading', <button onclick="" id="start-btn" style="max-width: 12rem; white-space: nowrap;" class="btn btn-secondary m-1 flex-grow-1 disabled"><i class="fa fa-spinner fa-spin"></i> {{ translate('serverTerm', 'importing',
data['lang']) }}</button> data['lang']) }}</button>
<button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart', data['lang']) %}</button> <button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart', data['lang']) %}</button>
<button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button> <button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button>

View File

@ -0,0 +1,16 @@
# Generated by database migrator
import peewee
def migrate(migrator, database, **kwargs):
migrator.add_columns("roles", manager=peewee.IntegerField(null=True))
"""
Write your migrations here.
"""
def rollback(migrator, database, **kwargs):
migrator.drop_columns("roles", ["manager"])
"""
Write your rollback migrations here.
"""

View File

@ -0,0 +1,16 @@
# Generated by database migrator
import peewee
def migrate(migrator, database, **kwargs):
migrator.add_columns("users", manager=peewee.IntegerField(null=True))
"""
Write your migrations here.
"""
def rollback(migrator, database, **kwargs):
migrator.drop_columns("users", ["manager"])
"""
Write your rollback migrations here.
"""

View File

@ -0,0 +1,16 @@
# Generated by database migrator
import peewee
def migrate(migrator, database, **kwargs):
migrator.add_columns("servers", created_by=peewee.IntegerField(default=-100))
"""
Write your migrations here.
"""
def rollback(migrator, database, **kwargs):
migrator.drop_columns("servers", ["created_by"])
"""
Write your rollback migrations here.
"""

View File

@ -0,0 +1,20 @@
# Generated by database migrator
import peewee
def migrate(migrator, database, **kwargs):
migrator.drop_columns("user_crafty", ["created_server"])
migrator.drop_columns("user_crafty", ["created_user"])
migrator.drop_columns("user_crafty", ["created_role"])
"""
Write your migrations here.
"""
def rollback(migrator, database, **kwargs):
migrator.add_columns("user_crafty", created_server=peewee.IntegerField(default=0))
migrator.add_columns("user_crafty", created_user=peewee.IntegerField(default=0))
migrator.add_columns("user_crafty", created_role=peewee.IntegerField(default=0))
"""
Write your rollback migrations here.
"""

View File

@ -0,0 +1,16 @@
# Generated by database migrator
import peewee
def migrate(migrator, database, **kwargs):
migrator.add_columns("servers", shutdown_timeout=peewee.IntegerField(default=60))
"""
Write your migrations here.
"""
def rollback(migrator, database, **kwargs):
migrator.drop_columns("servers", ["shutdown_timeout"])
"""
Write your rollback migrations here.
"""

View File

@ -0,0 +1,17 @@
# Generated by database migrator
import peewee
def migrate(migrator, database, **kwargs):
migrator.rename_column("server_stats", "downloading", "importing")
"""
Write your migrations here.
"""
def rollback(migrator, database, **kwargs):
migrator.rename_column("server_stats", "importing", "downloading")
"""
Write your rollback migrations here.
"""

View File

@ -245,7 +245,8 @@
"roleUsers": "Role Users: ", "roleUsers": "Role Users: ",
"serverAccess": "Access?", "serverAccess": "Access?",
"serverName": "Server Name", "serverName": "Server Name",
"serversDesc": "servers this role is allowed to access" "serversDesc": "servers this role is allowed to access",
"selectManager": "Select a manager for this Role"
}, },
"serverBackups": { "serverBackups": {
"backupAtMidnight": "Auto-backup at midnight?", "backupAtMidnight": "Auto-backup at midnight?",
@ -324,7 +325,10 @@
"stopBeforeDeleting": "Please stop the server before deleting it", "stopBeforeDeleting": "Please stop the server before deleting it",
"update": "Update Executable", "update": "Update Executable",
"yesDelete": "Yes, delete", "yesDelete": "Yes, delete",
"yesDeleteFiles": "Yes, delete files" "yesDeleteFiles": "Yes, delete files",
"shutdownTimeout": "Shutdown Timeout",
"timeoutExplain1": "How long Crafty will wait for your server to shutdown after executing the",
"timeoutExplain2": "command before it forces the process down."
}, },
"serverConfigHelp": { "serverConfigHelp": {
"desc": "Here is where you can change the configuration of your server", "desc": "Here is where you can change the configuration of your server",
@ -464,7 +468,7 @@
"serverTerm": { "serverTerm": {
"commandInput": "Enter your command", "commandInput": "Enter your command",
"delay-explained": "The service/agent has recently started and is delaying the start of the minecraft server instance", "delay-explained": "The service/agent has recently started and is delaying the start of the minecraft server instance",
"downloading": "Downloading...", "importing": "Importing...",
"restart": "Restart", "restart": "Restart",
"sendCommand": "Send command", "sendCommand": "Send command",
"start": "Start", "start": "Start",
@ -562,6 +566,8 @@
"userRoles": "User Roles", "userRoles": "User Roles",
"userRolesDesc": "Roles this user is a member of.", "userRolesDesc": "Roles this user is a member of.",
"userSettings": "User Settings", "userSettings": "User Settings",
"uses": "Number of uses allowed (-1==No Limit)" "uses": "Number of uses allowed (-1==No Limit)",
"manager": "Manager",
"selectManager": "Select Manager for User"
} }
} }

View File

@ -14,6 +14,7 @@ from app.classes.shared.import3 import Import3
from app.classes.shared.console import Console from app.classes.shared.console import Console
from app.classes.shared.helpers import Helpers from app.classes.shared.helpers import Helpers
from app.classes.models.users import HelperUsers from app.classes.models.users import HelperUsers
from app.classes.shared.import_helper import ImportHelpers
console = Console() console = Console()
helper = Helpers() helper = Helpers()
@ -135,8 +136,9 @@ if __name__ == "__main__":
else: else:
Console.debug("Existing install detected") Console.debug("Existing install detected")
file_helper = FileHelpers(helper) file_helper = FileHelpers(helper)
import_helper = ImportHelpers(helper, file_helper)
# now the tables are created, we can load the tasks_manager and server controller # now the tables are created, we can load the tasks_manager and server controller
controller = Controller(database, helper, file_helper) controller = Controller(database, helper, file_helper, import_helper)
import3 = Import3(helper, controller) import3 = Import3(helper, controller)
tasks_manager = TasksManager(helper, controller) tasks_manager = TasksManager(helper, controller)
tasks_manager.start_webserver() tasks_manager.start_webserver()