crafty-4/app/classes/controllers/servers_controller.py

592 lines
21 KiB
Python
Raw Normal View History

2021-09-08 22:01:10 +00:00
import os
import logging
2022-05-26 12:50:20 +00:00
import time
import json
import pathlib
import typing as t
2021-09-08 22:01:10 +00:00
from app.classes.controllers.roles_controller import RolesController
from app.classes.shared.file_helpers import FileHelpers
2022-05-26 12:50:20 +00:00
2022-05-26 21:19:59 +00:00
from app.classes.shared.singleton import Singleton
from app.classes.shared.server import ServerInstance
2022-05-26 12:50:20 +00:00
from app.classes.shared.console import Console
from app.classes.shared.helpers import Helpers
from app.classes.shared.main_models import DatabaseShortcuts
from app.classes.minecraft.stats import Stats
from app.classes.models.servers import HelperServers
from app.classes.models.users import HelperUsers, ApiKeys
from app.classes.models.server_permissions import (
PermissionsServers,
EnumPermissionsServer,
)
2023-11-03 20:41:23 +00:00
from app.classes.shared.websocket_manager import WebSocketManager
2021-09-08 22:01:10 +00:00
logger = logging.getLogger(__name__)
2022-05-26 12:50:20 +00:00
class ServersController(metaclass=Singleton):
servers_list: ServerInstance
2022-05-26 12:50:20 +00:00
def __init__(self, helper, servers_helper, management_helper, file_helper):
2022-05-26 13:02:47 +00:00
self.helper: Helpers = helper
self.file_helper: FileHelpers = file_helper
2022-05-26 12:50:20 +00:00
self.servers_helper: HelperServers = servers_helper
self.management_helper = management_helper
self.servers_list = []
self.stats = Stats(self.helper, self)
2023-11-30 17:22:40 +00:00
self.web_sock = WebSocketManager()
self.server_subpage = {}
2021-09-29 18:57:04 +00:00
# **********************************************************************************
2021-09-08 22:01:10 +00:00
# Generic Servers Methods
# **********************************************************************************
def create_server(
self,
name: str,
server_uuid: str,
server_dir: str,
server_command: str,
server_file: str,
server_log_file: str,
server_stop: str,
2022-02-10 20:27:40 +00:00
server_type: str,
created_by: int,
server_port: int = 25565,
2022-05-05 11:02:23 +00:00
server_host: str = "127.0.0.1",
) -> int:
"""Create a server in the database
Args:
name: The name of the server
server_uuid: This is the UUID of the server
server_dir: The directory where the server is located
backup_path: The path to the backup folder
server_command: The command to start the server
server_file: The name of the server file
server_log_file: The path to the server log file
server_stop: This is the command to stop the server
server_type: This is the type of server you're creating.
2022-05-05 11:02:23 +00:00
server_port: The port the server will be monitored on, defaults to 25565
server_host: The host the server will be monitored on, defaults to 127.0.0.1
Returns:
int: The new server's id
Raises:
PeeweeException: If the server already exists
"""
return HelperServers.create_server(
server_uuid,
2023-11-01 17:44:56 +00:00
name,
server_dir,
server_command,
server_file,
server_log_file,
server_stop,
2022-02-10 20:27:40 +00:00
server_type,
created_by,
server_port,
2022-05-05 11:02:23 +00:00
server_host,
)
2021-09-08 22:01:10 +00:00
@staticmethod
def get_server_obj(server_id):
return HelperServers.get_server_obj(server_id)
@staticmethod
def update_server(server_obj):
ret = HelperServers.update_server(server_obj)
2022-06-12 15:13:39 +00:00
server_instance: ServerInstance = ServersController().get_server_instance_by_id(
server_obj.server_id
)
server_instance.update_server_instance()
2023-01-30 23:35:04 +00:00
return ret
2023-09-19 19:46:31 +00:00
def get_history_stats(self, server_id, hours):
2022-08-21 21:04:23 +00:00
srv = ServersController().get_server_instance_by_id(server_id)
2023-09-19 19:46:31 +00:00
return srv.stats_helper.get_history_stats(server_id, hours)
2022-08-21 21:04:23 +00:00
2022-08-06 00:04:34 +00:00
@staticmethod
def update_unloaded_server(server_obj):
ret = HelperServers.update_server(server_obj)
return ret
2022-03-04 00:36:36 +00:00
@staticmethod
def set_import(server_id):
srv = ServersController().get_server_instance_by_id(server_id)
return srv.stats_helper.set_import()
2022-03-04 00:36:36 +00:00
@staticmethod
def finish_import(server_id, forge=False):
srv = ServersController().get_server_instance_by_id(server_id)
# This is where we start the forge installerr
if forge:
srv.run_threaded_server(
HelperUsers.get_user_id_by_name("system"), forge_install=True
)
else:
srv.stats_helper.finish_import()
2022-03-04 00:36:36 +00:00
@staticmethod
def get_import_status(server_id):
server = ServersController().get_server_instance_by_id(server_id)
return server.stats_helper.get_import_status()
2022-03-04 00:36:36 +00:00
def remove_server(self, server_id):
roles_list = PermissionsServers.get_roles_from_server(server_id)
2021-09-29 18:57:04 +00:00
for role in roles_list:
role_id = role.role_id
role_data = RolesController.get_role_with_servers(role_id)
role_data["servers"] = {server_id}
# Remove server id permissions from role
PermissionsServers.delete_roles_permissions(role_id, role_data["servers"])
# Remove roles from server
PermissionsServers.remove_roles_of_server(server_id)
self.management_helper.remove_all_server_backups(server_id)
# Finally remove server
self.servers_helper.remove_server(server_id)
2021-09-08 22:01:10 +00:00
@staticmethod
def get_server_data_by_id(server_id):
return HelperServers.get_server_data_by_id(server_id)
2021-09-08 22:01:10 +00:00
# **********************************************************************************
2021-09-08 22:01:10 +00:00
# Servers Methods
# **********************************************************************************
2022-05-26 12:50:20 +00:00
2023-11-01 17:44:56 +00:00
def get_server_instance_by_id(self, server_id: t.Union[str, str]) -> ServerInstance:
2022-05-26 12:50:20 +00:00
for server in self.servers_list:
2023-11-01 17:44:56 +00:00
if server["server_id"] == server_id:
2022-05-26 12:50:20 +00:00
return server["server_obj"]
2022-05-30 17:28:39 +00:00
logger.warning(f"Unable to find server object for server id {server_id}")
2023-02-15 23:40:20 +00:00
raise ValueError(f"Unable to find server object for server id {server_id}")
2022-05-26 12:50:20 +00:00
def init_all_servers(self):
servers = self.get_all_defined_servers()
self.failed_servers = []
2022-05-26 12:50:20 +00:00
for server in servers:
2023-11-30 17:22:40 +00:00
self.web_sock.broadcast_to_admins(
2023-11-30 05:55:26 +00:00
"update",
2023-11-30 17:14:09 +00:00
{"section": "server", "server": server["server_name"]},
)
2023-11-30 17:22:40 +00:00
self.web_sock.broadcast_to_non_admins(
2023-11-30 17:14:09 +00:00
"update",
{"section": "init"},
2023-11-03 20:41:23 +00:00
)
2022-05-26 12:50:20 +00:00
server_id = server.get("server_id")
# if we have already initialized this server, let's skip it.
if self.check_server_loaded(server_id):
continue
# if this server path no longer exists - let's warn and bomb out
if not Helpers.check_path_exists(
Helpers.get_os_understandable_path(server["path"])
):
logger.warning(
f"Unable to find server "
f"{server['server_name']} at path {server['path']}. "
f"Skipping this server"
)
Console.warning(
f"Unable to find server "
f"{server['server_name']} at path {server['path']}. "
f"Skipping this server"
)
if server not in self.failed_servers:
self.failed_servers.append(server)
2022-05-26 12:50:20 +00:00
continue
temp_server_dict = {
"server_id": server.get("server_id"),
"server_data_obj": server,
"server_obj": ServerInstance(
2022-05-26 12:50:20 +00:00
server.get("server_id"),
self.helper,
self.management_helper,
self.stats,
self.file_helper,
2022-05-26 12:50:20 +00:00
),
}
# setup the server, do the auto start and all that jazz
temp_server_dict["server_obj"].do_server_setup(server)
# add this temp object to the list of init servers
self.servers_list.append(temp_server_dict)
if server["auto_start"]:
2022-05-30 05:36:25 +00:00
self.set_waiting_start(server["server_id"], True)
2022-05-26 12:50:20 +00:00
self.refresh_server_settings(server["server_id"])
Console.info(
f"Loaded Server: ID {server['server_id']}"
f" | Name: {server['server_name']}"
f" | Autostart: {server['auto_start']}"
f" | Delay: {server['auto_start_delay']}"
)
def check_server_loaded(self, server_id_to_check: int):
logger.info(f"Checking to see if we already registered {server_id_to_check}")
for server in self.servers_list:
known_server = server.get("server_id")
if known_server is None:
return False
if known_server == server_id_to_check:
logger.info(
f"skipping initialization of server {server_id_to_check} "
f"because it is already loaded"
)
return True
return False
def refresh_server_settings(self, server_id: int):
2022-05-30 20:05:36 +00:00
server_obj = self.get_server_instance_by_id(server_id)
2022-05-26 12:50:20 +00:00
server_obj.reload_server_settings()
2021-09-08 22:01:10 +00:00
@staticmethod
def get_all_defined_servers():
return HelperServers.get_all_defined_servers()
2021-09-08 22:01:10 +00:00
@staticmethod
def get_authorized_servers(user_id):
2023-04-12 17:03:44 +00:00
server_ids = []
2022-06-12 16:39:05 +00:00
server_data: t.List[t.Dict[str, t.Any]] = []
user_roles = HelperUsers.user_role_query(user_id)
for user in user_roles:
role_servers = PermissionsServers.get_role_servers_from_role_id(
user.role_id
)
2021-09-08 22:01:10 +00:00
for role in role_servers:
2023-04-12 17:03:44 +00:00
if role.server_id.server_id not in server_ids:
server_ids.append(role.server_id.server_id)
server_data.append(
ServersController().get_server_instance_by_id(
role.server_id.server_id
)
2022-05-26 21:19:59 +00:00
)
2021-09-08 22:01:10 +00:00
return server_data
@staticmethod
def get_authorized_users(server_id: str):
user_ids: t.Set[int] = set()
roles_list = PermissionsServers.get_roles_from_server(server_id)
for role in roles_list:
role_users = HelperUsers.get_users_from_role(role.role_id)
for user_role in role_users:
2023-03-09 16:06:55 +00:00
user_ids.add(user_role.user_id.user_id)
for user_id in HelperUsers.get_super_user_list():
user_ids.add(user_id)
return user_ids
2022-05-26 12:50:20 +00:00
def get_all_servers_stats(self):
server_data = []
try:
for server in self.servers_list:
2022-06-12 15:13:39 +00:00
srv: ServerInstance = ServersController().get_server_instance_by_id(
2022-05-26 12:50:20 +00:00
server.get("server_id")
)
latest = srv.stats_helper.get_latest_server_stats()
server_data.append(
{
2022-06-12 15:13:39 +00:00
"server_data": DatabaseShortcuts.get_data_obj(
srv.server_object
),
2022-05-26 12:50:20 +00:00
"stats": latest,
"user_command_permission": True,
}
)
except IndexError as ex:
logger.error(
f"Stats collection failed with error: {ex}. Was a server just created?"
)
return server_data
2021-09-08 22:01:10 +00:00
2022-01-15 00:23:50 +00:00
@staticmethod
def get_authorized_servers_stats_api_key(api_key: ApiKeys):
server_data = []
2022-05-30 20:05:36 +00:00
authorized_servers = ServersController().get_authorized_servers(
api_key.user_id # TODO: API key authorized servers?
)
2022-01-15 00:23:50 +00:00
for server in authorized_servers:
srv: ServerInstance = server
2022-05-26 12:50:20 +00:00
latest = srv.stats_helper.get_latest_server_stats()
key_permissions = PermissionsServers.get_api_key_permissions_list(
2022-05-26 21:19:59 +00:00
api_key, server.server_id
)
if EnumPermissionsServer.COMMANDS in key_permissions:
2022-01-15 00:23:50 +00:00
user_command_permission = True
else:
user_command_permission = False
server_data.append(
{
2022-05-26 21:19:59 +00:00
"server_data": DatabaseShortcuts.get_data_obj(server.server_object),
2022-05-26 13:43:24 +00:00
"stats": latest,
"user_command_permission": user_command_permission,
}
)
2022-01-15 00:23:50 +00:00
return server_data
2021-09-08 22:01:10 +00:00
@staticmethod
def get_authorized_servers_stats(user_id):
server_data = []
authorized_servers = ServersController.get_authorized_servers(user_id)
2021-09-08 22:01:10 +00:00
for server in authorized_servers:
srv: ServerInstance = server
2022-05-26 12:50:20 +00:00
latest = srv.stats_helper.get_latest_server_stats()
2022-01-15 00:23:50 +00:00
# TODO
user_permissions = PermissionsServers.get_user_id_permissions_list(
2022-05-26 21:19:59 +00:00
user_id, server.server_id
)
if EnumPermissionsServer.COMMANDS in user_permissions:
2021-09-08 22:01:10 +00:00
user_command_permission = True
else:
user_command_permission = False
server_data.append(
{
2022-06-12 15:13:39 +00:00
"server_data": DatabaseShortcuts.get_data_obj(srv.server_object),
2022-05-26 10:49:51 +00:00
"stats": latest,
"user_command_permission": user_command_permission,
}
)
2022-01-15 00:23:50 +00:00
2021-09-08 22:01:10 +00:00
return server_data
@staticmethod
def get_server_friendly_name(server_id):
return HelperServers.get_server_friendly_name(server_id)
2021-09-08 22:01:10 +00:00
2022-05-26 12:50:20 +00:00
def crash_detection(self, server_obj):
svr = self.get_server_instance_by_id(server_obj.server_id)
2022-05-26 12:50:20 +00:00
# start or stop crash detection depending upon user preference
# The below functions check to see if the server is running.
# They only execute if it's running.
if server_obj.crash_detection == 1:
svr.start_crash_detection()
else:
svr.stop_crash_detection()
def get_server_obj_optional(
self, server_id: t.Union[str, int]
) -> t.Optional[ServerInstance]:
2022-05-26 12:50:20 +00:00
for server in self.servers_list:
if str(server["server_id"]) == str(server_id):
return server["server_obj"]
logger.warning(f"Unable to find server object for server id {server_id}")
return None
def get_server_data(self, server_id: str):
for server in self.servers_list:
if str(server["server_id"]) == str(server_id):
return server["server_data_obj"]
logger.warning(f"Unable to find server object for server id {server_id}")
return False
2022-05-30 20:05:36 +00:00
def list_defined_servers(self):
defined_servers = []
for server in self.servers_list:
defined_servers.append(
self.get_server_instance_by_id(server.get("server_id"))
)
return defined_servers
2022-05-26 12:50:20 +00:00
@staticmethod
def get_all_server_ids() -> t.List[int]:
return HelperServers.get_all_server_ids()
def list_running_servers(self):
running_servers = []
# for each server
2022-05-30 17:28:39 +00:00
for server in self.servers_list:
2022-05-26 12:50:20 +00:00
# is the server running?
srv_obj: ServerInstance = server["server_obj"]
2022-05-26 12:50:20 +00:00
running = srv_obj.check_running()
# if so, let's add a dictionary to the list of running servers
if running:
running_servers.append({"id": srv_obj.server_id, "name": srv_obj.name})
return running_servers
def stop_all_servers(self):
servers = self.list_running_servers()
logger.info(f"Found {len(servers)} running server(s)")
Console.info(f"Found {len(servers)} running server(s)")
logger.info("Stopping All Servers")
Console.info("Stopping All Servers")
for server in servers:
logger.info(f"Stopping Server ID {server['id']} - {server['name']}")
Console.info(f"Stopping Server ID {server['id']} - {server['name']}")
self.stop_server(server["id"])
# let's wait 2 seconds to let everything flush out
time.sleep(2)
logger.info("All Servers Stopped")
Console.info("All Servers Stopped")
def stop_server(self, server_id):
# issue the stop command
2022-05-30 17:28:39 +00:00
svr_obj = self.get_server_instance_by_id(server_id)
2022-05-26 12:50:20 +00:00
svr_obj.stop_threaded_server()
# **********************************************************************************
2021-09-08 22:01:10 +00:00
# Servers_Stats Methods
# **********************************************************************************
2021-09-08 22:01:10 +00:00
@staticmethod
def get_server_stats_by_id(server_id):
srv = ServersController().get_server_instance_by_id(server_id)
2022-05-26 12:50:20 +00:00
return srv.stats_helper.get_latest_server_stats()
2021-09-08 22:01:10 +00:00
@staticmethod
def server_id_exists(server_id):
try:
srv = ServersController().get_server_instance_by_id(server_id)
except ValueError:
return False
2022-05-26 12:50:20 +00:00
return srv.stats_helper.server_id_exists()
2021-09-08 22:01:10 +00:00
@staticmethod
def get_server_type_by_id(server_id):
return HelperServers.get_server_type_by_id(server_id)
2021-09-08 22:01:10 +00:00
@staticmethod
2022-01-15 00:23:50 +00:00
def server_id_authorized(server_id_a, user_id):
user_roles = HelperUsers.user_role_query(user_id)
2021-09-08 22:01:10 +00:00
for role in user_roles:
for server_id_b in PermissionsServers.get_role_servers_from_role_id(
role.role_id
):
if str(server_id_a) == str(server_id_b.server_id):
2022-01-15 00:23:50 +00:00
return True
return False
2021-09-08 22:01:10 +00:00
2022-01-28 01:43:23 +00:00
@staticmethod
def is_crashed(server_id):
srv = ServersController().get_server_instance_by_id(server_id)
2022-05-26 12:50:20 +00:00
return srv.stats_helper.is_crashed()
2022-01-28 01:43:23 +00:00
2022-01-15 00:23:50 +00:00
@staticmethod
def server_id_authorized_api_key(server_id: str, api_key: ApiKeys) -> bool:
# TODO
return ServersController.server_id_authorized(server_id, api_key.user.user_id)
2022-01-15 00:23:50 +00:00
# There is no view server permission
# permission_helper.both_have_perm(api_key)
2021-09-08 22:01:10 +00:00
@staticmethod
def set_update(server_id, value):
srv = ServersController().get_server_instance_by_id(server_id)
2022-05-26 12:50:20 +00:00
return srv.stats_helper.set_update(value)
2021-09-08 22:01:10 +00:00
@staticmethod
def get_ttl_without_player(server_id):
srv = ServersController().get_server_instance_by_id(server_id)
2022-05-26 12:50:20 +00:00
return srv.stats_helper.get_ttl_without_player()
2021-09-08 22:01:10 +00:00
@staticmethod
def can_stop_no_players(server_id, time_limit):
srv = ServersController().get_server_instance_by_id(server_id)
2022-05-26 12:50:20 +00:00
return srv.stats_helper.can_stop_no_players(time_limit)
2021-09-08 22:01:10 +00:00
@staticmethod
def set_waiting_start(server_id, value):
srv = ServersController().get_server_instance_by_id(server_id)
2022-05-26 12:50:20 +00:00
srv.stats_helper.set_waiting_start(value)
2021-09-21 19:13:17 +00:00
@staticmethod
def get_waiting_start(server_id):
srv = ServersController().get_server_instance_by_id(server_id)
2022-05-26 12:50:20 +00:00
return srv.stats_helper.get_waiting_start()
2021-09-29 18:57:04 +00:00
@staticmethod
def get_update_status(server_id):
srv = ServersController().get_server_instance_by_id(server_id)
2022-05-26 12:50:20 +00:00
return srv.stats_helper.get_update_status()
# **********************************************************************************
2021-09-08 22:01:10 +00:00
# Servers Helpers Methods
# **********************************************************************************
2023-04-15 18:47:35 +00:00
@staticmethod
def get_cached_players(server_id):
srv = ServersController().get_server_instance_by_id(server_id)
stats = srv.stats_helper.get_server_stats()
server_path = stats["server_id"]["path"]
path = os.path.join(server_path, "usercache.json")
try:
with open(
Helpers.get_os_understandable_path(path), encoding="utf-8"
) as file:
content = file.read()
file.close()
except Exception as ex:
logger.error(ex)
return {}
2023-04-15 18:47:35 +00:00
return json.loads(content)
2021-09-08 22:01:10 +00:00
@staticmethod
def get_banned_players(server_id):
srv = ServersController().get_server_instance_by_id(server_id)
2022-05-26 12:50:20 +00:00
stats = srv.stats_helper.get_server_stats()
server_path = stats["server_id"]["path"]
path = os.path.join(server_path, "banned-players.json")
2021-09-08 22:01:10 +00:00
try:
with open(
Helpers.get_os_understandable_path(path), encoding="utf-8"
) as file:
2021-09-08 22:01:10 +00:00
content = file.read()
file.close()
except Exception as ex:
logger.error(ex)
return {}
2021-09-29 18:57:04 +00:00
2021-09-08 22:01:10 +00:00
return json.loads(content)
2021-09-29 18:57:04 +00:00
2021-09-08 22:01:10 +00:00
def check_for_old_logs(self):
servers = HelperServers.get_all_defined_servers()
2021-09-08 22:01:10 +00:00
for server in servers:
logs_path, latest_log_file = os.path.split(server["log_path"])
logs_delete_after = int(server["logs_delete_after"])
2021-09-08 22:01:10 +00:00
if logs_delete_after == 0:
continue
log_files = list(
filter(
lambda val: val != latest_log_file,
os.listdir(
pathlib.Path(
server["path"], os.path.split(server["log_path"])[0]
)
),
)
)
2021-09-08 22:01:10 +00:00
for log_file in log_files:
log_file_path = os.path.join(logs_path, log_file)
if Helpers.check_file_exists(
log_file_path
) and Helpers.is_file_older_than_x_days(
log_file_path, logs_delete_after
):
2021-09-08 22:01:10 +00:00
os.remove(log_file_path)