Add the server creation endpoint

This commit is contained in:
luukas 2022-05-05 14:02:23 +03:00
parent 53459d83dc
commit 721c9cfe12
5 changed files with 267 additions and 23 deletions

View File

@ -35,6 +35,7 @@ class ServersController:
server_stop: str,
server_type: str,
server_port: int = 25565,
server_host: str = "127.0.0.1",
) -> int:
"""Create a server in the database
@ -48,7 +49,8 @@ class ServersController:
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.
server_port: The port the server will run on, defaults to 25565 (optional)
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
@ -67,6 +69,7 @@ class ServersController:
server_stop,
server_type,
server_port,
server_host,
)
@staticmethod

View File

@ -95,6 +95,7 @@ class HelperServers:
server_stop: str,
server_type: str,
server_port: int = 25565,
server_host: str = "127.0.0.1",
) -> int:
"""Create a server in the database
@ -108,7 +109,8 @@ class HelperServers:
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.
server_port: The port the server will run on, defaults to 25565 (optional)
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
@ -128,6 +130,7 @@ class HelperServers:
Servers.crash_detection: False,
Servers.log_path: server_log_file,
Servers.server_port: server_port,
Servers.server_ip: server_host,
Servers.stop_command: server_stop,
Servers.backup_path: backup_path,
Servers.type: server_type,

View File

@ -72,7 +72,7 @@ class Helpers:
installer.do_install()
@staticmethod
def float_to_string(gbs: int):
def float_to_string(gbs: float):
s = str(float(gbs) * 1000).rstrip("0").rstrip(".")
return s
@ -232,7 +232,7 @@ class Helpers:
return default_return
with open(self.settings_file, "w", encoding="utf-8") as f:
json.dump(data, f, indent=1)
json.dump(data, f, indent=2)
except Exception as e:
logger.critical(

View File

@ -337,6 +337,177 @@ class Controller:
svr_obj = self.get_server_obj(server_id)
svr_obj.stop_threaded_server()
def create_api_server(self, data: dict):
server_fs_uuid = Helpers.create_uuid()
new_server_path = os.path.join(self.helper.servers_dir, server_fs_uuid)
backup_path = os.path.join(self.helper.backup_path, server_fs_uuid)
if Helpers.is_os_windows():
new_server_path = Helpers.wtol_path(new_server_path)
backup_path = Helpers.wtol_path(backup_path)
new_server_path.replace(" ", "^ ")
backup_path.replace(" ", "^ ")
Helpers.ensure_dir_exists(new_server_path)
Helpers.ensure_dir_exists(backup_path)
def _copy_import_dir_files(existing_server_path):
existing_server_path = Helpers.get_os_understandable_path(
existing_server_path
)
try:
FileHelpers.copy_dir(existing_server_path, new_server_path, True)
except shutil.Error as ex:
logger.error(f"Server import failed with error: {ex}")
def _create_server_properties_if_needed(port, empty=False):
properties_file = os.path.join(new_server_path, "server.properties")
has_properties = os.path.exists(properties_file)
if not has_properties:
logger.info(
f"No server.properties found on import."
f"Creating one with port selection of {port}"
)
with open(
properties_file,
"w",
encoding="utf-8",
) as file:
file.write(
"# generated by Crafty Controller"
+ ("" if empty else f"\nserver-port={port}")
)
root_create_data = data[data["create_type"] + "_create_data"]
create_data = root_create_data[root_create_data["create_type"] + "_create_data"]
if data["create_type"] == "minecraft_java":
if root_create_data["create_type"] == "download_jar":
server_file = f"{create_data['type']}-{create_data['version']}.jar"
full_jar_path = os.path.join(new_server_path, server_file)
# Create an EULA file
with open(
os.path.join(new_server_path, "eula.txt"), "w", encoding="utf-8"
) as file:
file.write(
"eula=" + ("true" if create_data["agree_to_eula"] else "false")
)
elif root_create_data["create_type"] == "import_server":
_copy_import_dir_files(create_data["existing_server_path"])
full_jar_path = os.path.join(new_server_path, create_data["jarfile"])
elif root_create_data["create_type"] == "import_zip":
# TODO: Copy files from the zip file to the new server directory
full_jar_path = os.path.join(new_server_path, create_data["jarfile"])
raise Exception("Not yet implemented")
_create_server_properties_if_needed(create_data["server_properties_port"])
min_mem = create_data["mem_min"]
max_mem = create_data["mem_max"]
def _gibs_to_mibs(gibs: float) -> str:
return str(int(gibs * 1024))
def _wrap_jar_if_windows():
return (
f'"{full_jar_path}"' if Helpers.is_os_windows() else full_jar_path
)
server_command = (
f"java -Xms{_gibs_to_mibs(min_mem)}M "
f"-Xmx{_gibs_to_mibs(max_mem)}M "
f"-jar {_wrap_jar_if_windows()} nogui"
)
elif data["create_type"] == "minecraft_bedrock":
if root_create_data["create_type"] == "import_server":
existing_server_path = Helpers.get_os_understandable_path(
create_data["existing_server_path"]
)
try:
FileHelpers.copy_dir(existing_server_path, new_server_path, True)
except shutil.Error as ex:
logger.error(f"Server import failed with error: {ex}")
elif root_create_data["create_type"] == "import_zip":
# TODO: Copy files from the zip file to the new server directory
raise Exception("Not yet implemented")
_create_server_properties_if_needed(0, True)
server_command = create_data["command"]
server_file = ""
elif data["create_type"] == "custom":
# TODO: working_directory, executable_update
if root_create_data["create_type"] == "raw_exec":
pass
elif root_create_data["create_type"] == "import_server":
existing_server_path = Helpers.get_os_understandable_path(
create_data["existing_server_path"]
)
try:
FileHelpers.copy_dir(existing_server_path, new_server_path, True)
except shutil.Error as ex:
logger.error(f"Server import failed with error: {ex}")
elif root_create_data["create_type"] == "import_zip":
# TODO: Copy files from the zip file to the new server directory
raise Exception("Not yet implemented")
_create_server_properties_if_needed(0, True)
server_command = create_data["command"]
server_file = root_create_data["executable_update"].get("file", "")
stop_command = data.get("stop_command", "")
if stop_command == "":
# TODO: different default stop commands for server creation types
stop_command = "stop"
log_location = data.get("log_location", "")
if log_location == "":
# TODO: different default log locations for server creation types
log_location = "/logs/latest.log"
if data["monitoring_type"] == "minecraft_java":
monitoring_port = data["minecraft_java_monitoring_data"]["port"]
monitoring_host = data["minecraft_java_monitoring_data"]["host"]
monitoring_type = "minecraft-java"
elif data["monitoring_type"] == "minecraft_bedrock":
monitoring_port = data["minecraft_bedrock_monitoring_data"]["port"]
monitoring_host = data["minecraft_bedrock_monitoring_data"]["host"]
monitoring_type = "minecraft-bedrock"
elif data["monitoring_type"] == "none":
# TODO: this needs to be NUKED..
# There shouldn't be anything set if there are nothing to monitor
monitoring_port = 25565
monitoring_host = "127.0.0.1"
monitoring_type = "minecraft-java"
new_server_id = self.register_server(
name=data["name"],
server_uuid=server_fs_uuid,
server_dir=new_server_path,
backup_path=backup_path,
server_command=server_command,
server_file=server_file,
server_log_file=log_location,
server_stop=stop_command,
server_port=monitoring_port,
server_host=monitoring_host,
server_type=monitoring_type,
)
if (
data["create_type"] == "minecraft_java"
and root_create_data["create_type"] == "download_jar"
):
self.server_jars.download_jar(
create_data["type"],
create_data["version"],
full_jar_path,
new_server_id,
)
return new_server_id, server_fs_uuid
def create_jar_server(
self,
server: str,
@ -759,6 +930,7 @@ class Controller:
server_stop: str,
server_port: int,
server_type: str,
server_host: str = "127.0.0.1",
):
# put data in the db
new_id = self.servers.create_server(
@ -772,6 +944,7 @@ class Controller:
server_stop,
server_type,
server_port,
server_host,
)
if not Helpers.check_file_exists(
@ -788,7 +961,6 @@ class Controller:
"The server is managed by Crafty Controller.\n "
"Leave this directory/files alone please"
)
file.close()
except Exception as e:
logger.error(f"Unable to create required server files due to :{e}")

View File

@ -30,6 +30,7 @@ new_server_schema = {
"version": "1.18.2",
"mem_min": 1,
"mem_max": 2,
"server_properties_port": 25565,
},
},
}
@ -90,7 +91,6 @@ new_server_schema = {
"type": "string",
"default": "127.0.0.1",
"examples": ["127.0.0.1"],
"pattern": "^.*$",
},
"port": {
"title": "Port",
@ -142,7 +142,14 @@ new_server_schema = {
"download_jar_create_data": {
"title": "JAR download data",
"type": "object",
"required": ["type", "version", "mem_min", "mem_max"],
"required": [
"type",
"version",
"mem_min",
"mem_max",
"server_properties_port",
"agree_to_eula",
],
"properties": {
"type": {
"title": "Server JAR Type",
@ -155,19 +162,31 @@ new_server_schema = {
"examples": ["1.18.2"],
},
"mem_min": {
"title": "Minimum JVM memory",
"title": "Minimum JVM memory (in GiBs)",
"type": "number",
"examples": [1],
"default": 1,
"exclusiveMinimum": 0,
},
"mem_max": {
"title": "Maximum JVM memory",
"title": "Maximum JVM memory (in GiBs)",
"type": "number",
"examples": [2],
"default": 2,
"exclusiveMinimum": 0,
},
"server_properties_port": {
"title": "Port",
"type": "integer",
"examples": [25565],
"default": 25565,
"minimum": 0,
},
"agree_to_eula": {
"title": "Agree to the EULA",
"type": "boolean",
"default": False,
},
},
},
"import_server_create_data": {
@ -178,6 +197,8 @@ new_server_schema = {
"jarfile",
"mem_min",
"mem_max",
"server_properties_port",
"agree_to_eula",
],
"properties": {
"existing_server_path": {
@ -190,22 +211,34 @@ new_server_schema = {
"title": "JAR file",
"description": "The JAR file relative to the previous path",
"type": "string",
"examples": ["paper.jar"],
"examples": ["paper.jar", "jars/vanilla-1.12.jar"],
},
"mem_min": {
"title": "Minimum JVM memory",
"title": "Minimum JVM memory (in GiBs)",
"type": "number",
"examples": [1],
"default": 1,
"exclusiveMinimum": 0,
},
"mem_max": {
"title": "Maximum JVM memory",
"title": "Maximum JVM memory (in GiBs)",
"type": "number",
"examples": [2],
"default": 2,
"exclusiveMinimum": 0,
},
"server_properties_port": {
"title": "Port",
"type": "integer",
"examples": [25565],
"default": 25565,
"minimum": 0,
},
"agree_to_eula": {
"title": "Agree to the EULA",
"type": "boolean",
"default": False,
},
},
},
"import_zip_create_data": {
@ -217,6 +250,8 @@ new_server_schema = {
"jarfile",
"mem_min",
"mem_max",
"server_properties_port",
"agree_to_eula",
],
"properties": {
"zip_path": {
@ -229,7 +264,7 @@ new_server_schema = {
"title": "Server root directory",
"description": "The server root in the ZIP archive",
"type": "string",
"examples": ["/", "/paper-server/"],
"examples": ["/", "/paper-server/", "server-1"],
},
"jarfile": {
"title": "JAR file",
@ -238,19 +273,31 @@ new_server_schema = {
"examples": ["paper.jar", "jars/vanilla-1.12.jar"],
},
"mem_min": {
"title": "Minimum JVM memory",
"title": "Minimum JVM memory (in GiBs)",
"type": "number",
"examples": [1],
"default": 1,
"exclusiveMinimum": 0,
},
"mem_max": {
"title": "Maximum JVM memory",
"title": "Maximum JVM memory (in GiBs)",
"type": "number",
"examples": [2],
"default": 2,
"exclusiveMinimum": 0,
},
"server_properties_port": {
"title": "Port",
"type": "integer",
"examples": [25565],
"default": 25565,
"minimum": 0,
},
"agree_to_eula": {
"title": "Agree to the EULA",
"type": "boolean",
"default": False,
},
},
},
},
@ -333,7 +380,7 @@ new_server_schema = {
"title": "Server root directory",
"description": "The server root in the ZIP archive",
"type": "string",
"examples": ["/", "/paper-server/"],
"examples": ["/", "/paper-server/", "server-1"],
},
"command": {
"title": "Command",
@ -382,6 +429,7 @@ new_server_schema = {
"properties": {
"working_directory": {
"title": "Working directory",
"description": '"" means the default',
"type": "string",
"default": "",
"examples": ["/mnt/mydrive/server-configs/", "./subdirectory", ""],
@ -390,6 +438,7 @@ new_server_schema = {
"title": "Executable Updation",
"description": "Also configurable later on and for other servers",
"type": "object",
"required": ["enabled", "file", "url"],
"properties": {
"enabled": {
"title": "Enabled",
@ -413,7 +462,7 @@ new_server_schema = {
"title": "Creation type",
"type": "string",
"default": "raw_exec",
"enum": ["raw_exec", "import_exec", "import_zip"],
"enum": ["raw_exec", "import_server", "import_zip"],
},
"raw_exec_create_data": {
"title": "Raw execution command create data",
@ -462,7 +511,7 @@ new_server_schema = {
"title": "Server root directory",
"description": "The server root in the ZIP archive",
"type": "string",
"examples": ["/", "/paper-server/"],
"examples": ["/", "/paper-server/", "server-1"],
},
"command": {
"title": "Command",
@ -485,7 +534,9 @@ new_server_schema = {
},
{
"if": {
"properties": {"create_type": {"const": "import_exec"}}
"properties": {
"create_type": {"const": "import_server"}
}
},
"then": {"required": ["import_server_create_data"]},
},
@ -611,12 +662,21 @@ class ApiServersIndexHandler(BaseApiHandler):
},
)
# TODO: implement everything
new_server_id, new_server_uuid = self.controller.create_api_server(data)
# Increase the server creation counter
self.controller.crafty_perms.add_server_creation(user["user_id"])
self.controller.stats.record_stats()
self.controller.management.add_to_audit_log(
user["user_id"],
f"Created server {'1234'} (ID:{123})",
server_id=0,
(
f"created server {data['name']}"
f" (ID: {new_server_id})"
f" (UUID: {new_server_uuid})"
),
server_id=new_server_id,
source_ip=self.get_remote_ip(),
)
@ -624,5 +684,11 @@ class ApiServersIndexHandler(BaseApiHandler):
self.finish_json(
201,
{"status": "ok", "data": {"server_id": ""}},
{
"status": "ok",
"data": {
"new_server_id": str(new_server_id),
"new_server_uuid": new_server_uuid,
},
},
)