diff --git a/CHANGELOG.md b/CHANGELOG.md index fa2da068..4cdba0be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,38 @@ # Changelog - -## --- [4.0.8] - 2022/TBD +## --- [4.0.10] - 2022/TBD ### New features TBD ### Bug fixes +TBD +### Tweaks +TBD +### Lang +TBD +

+ +## --- [4.0.9] - 2022/08/06 +### Bug fixes +- Fix Schedules Traceback Bug ([Merge Request |](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/419) [Commit |](https://gitlab.com/crafty-controller/crafty-4/-/commit/f69d79b7023d6c26fccb5caeae9e47b40ebe5af2) [Commit](https://gitlab.com/crafty-controller/crafty-4/-/commit/ad318296dc93beb5533fcd13066440df9f9e799a)) +- Fix handling of missing servers ([Merge RequestπŸŽ‰](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/420)) +- Fix offline credits panel stack ([Commit](https://gitlab.com/crafty-controller/crafty-4/-/commit/247678e6c6af5e7d5dbfcf3bfdcec49fc5980e55)) +### Tweaks +- credits-v2| Translator status ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/421)) +- Use Names in Schedules ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/419)) +### Lang +- Make Schedules panel translatable ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/419)) +

+ +## --- [4.0.8] - 2022/08/05 +### New features +- Add Crafty Version Check and notification ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/411)) +### Bug fixes - Fix SU status not sticking on user creation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/410)) - Handle Missing Java From Win Registry ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/413)) - Disable restart while server is backing up ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/414)) +- Fix server creation with serverjars API ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/415)) +- Fix API Key delete confirmations ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/416)) ### Tweaks -TBD +- Add next run to schedule info ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/417)) ### Lang - Updated `es_ES` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/412)) - Added `pl_PL` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/412)) diff --git a/README.md b/README.md index 021d6f3f..1cd41069 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Supported Python Versions](https://shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20-blue)](https://www.python.org) -[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.8--beta-orange)](https://gitlab.com/crafty-controller/crafty-4/-/releases) +[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.10--beta-orange)](https://gitlab.com/crafty-controller/crafty-4/-/releases) [![Code Quality(temp-hardcoded)](https://img.shields.io/badge/code%20quality-10-brightgreen)](https://gitlab.com/crafty-controller/crafty-4) [![Build Status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master) -# Crafty Controller 4.0.8-beta +# Crafty Controller 4.0.10-beta > Python based Control Panel for your Minecraft Server ## What is Crafty Controller? diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py index b0b1f10a..547a8ebd 100644 --- a/app/classes/controllers/management_controller.py +++ b/app/classes/controllers/management_controller.py @@ -76,7 +76,7 @@ class ManagementController: interval_type, start_time, command, - comment=None, + name, enabled=True, ): return HelpersManagement.create_scheduled_task( @@ -86,7 +86,7 @@ class ManagementController: interval_type, start_time, command, - comment, + name, enabled, ) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 0e5f3f5f..650a16b0 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -102,6 +102,11 @@ class ServersController(metaclass=Singleton): server_instance.update_server_instance() return ret + @staticmethod + def update_unloaded_server(server_obj): + ret = HelperServers.update_server(server_obj) + return ret + @staticmethod def set_download(server_id): srv = ServersController().get_server_instance_by_id(server_id) @@ -146,6 +151,7 @@ class ServersController(metaclass=Singleton): def init_all_servers(self): servers = self.get_all_defined_servers() + self.failed_servers = [] for server in servers: server_id = server.get("server_id") @@ -169,6 +175,8 @@ class ServersController(metaclass=Singleton): f"{server['server_name']} at path {server['path']}. " f"Skipping this server" ) + if server not in self.failed_servers: + self.failed_servers.append(server) continue temp_server_dict = { diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py index b1fc9580..a5eb11ba 100644 --- a/app/classes/minecraft/serverjars.py +++ b/app/classes/minecraft/serverjars.py @@ -51,7 +51,7 @@ class ServerJars: def get_serverjar_data(self): data = self._read_cache() - return data.get("servers") + return data.get("types") def _check_api_alive(self): logger.info("Checking serverjars.com API status") @@ -70,6 +70,39 @@ class ServerJars: logger.error("unable to contact serverjars.com api") return False + def manual_refresh_cache(self): + cache_file = self.helper.serverjar_cache + + # debug override + # cache_old = True + + # if the API is down... we bomb out + if not self._check_api_alive(): + return False + + logger.info("Manual Refresh requested.") + now = datetime.now() + data = { + "last_refreshed": now.strftime("%m/%d/%Y, %H:%M:%S"), + "types": {}, + } + + jar_types = self._get_server_type_list() + data["types"].update(jar_types) + for s in data["types"]: + data["types"].update({s: dict.fromkeys(data["types"].get(s), {})}) + for j in data["types"].get(s): + versions = self._get_jar_details(j, s) + data["types"][s].update({j: versions}) + # save our cache + try: + with open(cache_file, "w", encoding="utf-8") as f: + f.write(json.dumps(data, indent=4)) + logger.info("Cache file refreshed") + + except Exception as e: + logger.error(f"Unable to update serverjars.com cache file: {e}") + def refresh_cache(self): cache_file = self.helper.serverjar_cache @@ -88,22 +121,18 @@ class ServerJars: if cache_old: logger.info("Cache file is over 1 day old, refreshing") now = datetime.now() - data = {"last_refreshed": now.strftime("%m/%d/%Y, %H:%M:%S"), "servers": {}} + data = { + "last_refreshed": now.strftime("%m/%d/%Y, %H:%M:%S"), + "types": {}, + } jar_types = self._get_server_type_list() - - # for each jar type - for j in jar_types: - - # for each server - for s in jar_types.get(j): - # jar versions for this server - versions = self._get_jar_details(s) - - # add these versions (a list) to the dict with - # a key of the server type - data["servers"].update({s: versions}) - + data["types"].update(jar_types) + for s in data["types"]: + data["types"].update({s: dict.fromkeys(data["types"].get(s), {})}) + for j in data["types"].get(s): + versions = self._get_jar_details(j, s) + data["types"][s].update({j: versions}) # save our cache try: with open(cache_file, "w", encoding="utf-8") as f: @@ -113,8 +142,8 @@ class ServerJars: except Exception as e: logger.error(f"Unable to update serverjars.com cache file: {e}") - def _get_jar_details(self, jar_type="servers"): - url = f"/api/fetchAll/{jar_type}" + def _get_jar_details(self, server_type, jar_type="servers"): + url = f"/api/fetchAll/{jar_type}/{server_type}" response = self._get_api_result(url) temp = [] for v in response: @@ -127,19 +156,19 @@ class ServerJars: response = self._get_api_result(url) return response - def download_jar(self, server, version, path, server_id): + def download_jar(self, jar, server, version, path, server_id): update_thread = threading.Thread( name=f"server_download-{server_id}-{server}-{version}", target=self.a_download_jar, daemon=True, - args=(server, version, path, server_id), + args=(jar, server, version, path, server_id), ) update_thread.start() - def a_download_jar(self, server, version, path, server_id): + def a_download_jar(self, jar, server, version, path, server_id): # delaying download for server register to finish time.sleep(3) - fetch_url = f"{self.base_url}/api/fetchJar/{server}/{version}" + fetch_url = f"{self.base_url}/api/fetchJar/{jar}/{server}/{version}" server_users = PermissionsServers.get_server_user_list(server_id) # We need to make sure the server is registered before diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 085bdd37..67d1ff9c 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -110,11 +110,12 @@ class Schedules(BaseModel): interval_type = CharField() start_time = CharField(null=True) command = CharField(null=True) - comment = CharField() + name = CharField() one_time = BooleanField(default=False) cron_string = CharField(default="") parent = IntegerField(null=True) delay = IntegerField(default=0) + next_run = CharField(default="") class Meta: table_name = "schedules" @@ -264,7 +265,7 @@ class HelpersManagement: interval_type, start_time, command, - comment=None, + name, enabled=True, one_time=False, cron_string="* * * * *", @@ -280,11 +281,12 @@ class HelpersManagement: Schedules.interval_type: interval_type, Schedules.start_time: start_time, Schedules.command: command, - Schedules.comment: comment, + Schedules.name: name, Schedules.one_time: one_time, Schedules.cron_string: cron_string, Schedules.parent: parent, Schedules.delay: delay, + Schedules.next_run: "", } ).execute() return sch_id diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index e1d9c3c0..40a57219 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -20,6 +20,7 @@ import itertools from datetime import datetime from socket import gethostname from contextlib import redirect_stderr, suppress +from packaging import version as pkg_version from app.classes.shared.null_writer import NullWriter from app.classes.shared.console import Console @@ -75,6 +76,7 @@ class Helpers: self.websocket_helper = WebSocketHelper(self) self.translation = Translation(self) + self.update_available = False @staticmethod def auto_installer_fix(ex): @@ -82,6 +84,29 @@ class Helpers: print(f"Import Error: Unable to load {ex.name} module") installer.do_install() + def check_remote_version(self): + """ + Check if the remote version is newer than the local version + Returning remote version if it is newer, otherwise False. + """ + try: + # Get tags from Gitlab, select the latest and parse the semver + response = get( + "https://gitlab.com/api/v4/projects/20430749/repository/tags" + ) + if response.status_code == 200: + remote_version = pkg_version.parse(json.loads(response.text)[0]["name"]) + + # Get local version data from the file and parse the semver + local_version = pkg_version.parse(self.get_version_string()) + + if remote_version > local_version: + return remote_version + + except Exception as e: + logger.error(f"Unable to check for new crafty version! \n{e}") + return False + @staticmethod def find_java_installs(): # If we're windows return oracle java versions, diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index c6c3cfb6..bca24fe8 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -422,6 +422,7 @@ class Controller: def create_jar_server( self, + jar: str, server: str, version: str, name: str, @@ -493,7 +494,7 @@ class Controller: # download the jar self.server_jars.download_jar( - server, version, os.path.join(server_dir, server_file), new_id + jar, server, version, os.path.join(server_dir, server_file), new_id ) return new_id @@ -933,6 +934,14 @@ class Controller: counter += 1 + def remove_unloaded_server(self, server_id): + try: + HelpersManagement.delete_scheduled_task_by_server(server_id) + except DoesNotExist: + logger.info("No scheduled jobs exist. Continuing.") + # remove the server from the DB + self.servers.remove_server(server_id) + @staticmethod def clear_unexecuted_commands(): HelpersManagement.clear_unexecuted_commands() diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index 9876abc2..7055d1cb 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -3,7 +3,7 @@ import time import logging import threading import asyncio -import datetime +from datetime import datetime from tzlocal import get_localzone from tzlocal.utils import ZoneInfoNotFoundError @@ -192,6 +192,15 @@ class TasksManager: def scheduler_thread(self): schedules = HelpersManagement.get_schedules_enabled() self.scheduler.add_listener(self.schedule_watcher, mask=EVENT_JOB_EXECUTED) + self.scheduler.start() + self.check_for_updates() + self.scheduler.add_job( + self.check_for_updates, + "interval", + hours=12, + id="update_watcher", + start_date=datetime.now(), + ) # self.scheduler.add_job( # self.scheduler.print_jobs, "interval", seconds=10, id="-1" # ) @@ -199,9 +208,10 @@ class TasksManager: # load schedules from DB for schedule in schedules: if schedule.interval != "reaction": + new_job = "error" if schedule.cron_string != "": try: - self.scheduler.add_job( + new_job = self.scheduler.add_job( HelpersManagement.add_command, CronTrigger.from_crontab( schedule.cron_string, timezone=str(self.tz) @@ -215,6 +225,7 @@ class TasksManager: ], ) except Exception as e: + new_job = "error" Console.error(f"Failed to schedule task with error: {e}.") Console.warning("Removing failed task from DB.") logger.error(f"Failed to schedule task with error: {e}.") @@ -225,7 +236,7 @@ class TasksManager: ) else: if schedule.interval_type == "hours": - self.scheduler.add_job( + new_job = self.scheduler.add_job( HelpersManagement.add_command, "cron", minute=0, @@ -239,7 +250,7 @@ class TasksManager: ], ) elif schedule.interval_type == "minutes": - self.scheduler.add_job( + new_job = self.scheduler.add_job( HelpersManagement.add_command, "cron", minute="*/" + str(schedule.interval), @@ -253,7 +264,7 @@ class TasksManager: ) elif schedule.interval_type == "days": curr_time = schedule.start_time.split(":") - self.scheduler.add_job( + new_job = self.scheduler.add_job( HelpersManagement.add_command, "cron", day="*/" + str(schedule.interval), @@ -267,7 +278,18 @@ class TasksManager: schedule.command, ], ) - self.scheduler.start() + if new_job != "error": + task = self.controller.management.get_scheduled_task_model( + int(new_job.id) + ) + self.controller.management.update_scheduled_task( + task.schedule_id, + { + "next_run": str( + new_job.next_run_time.strftime("%m/%d/%Y, %H:%M:%S") + ) + }, + ) jobs = self.scheduler.get_jobs() logger.info("Loaded schedules. Current enabled schedules: ") for item in jobs: @@ -281,7 +303,7 @@ class TasksManager: job_data["interval_type"], job_data["start_time"], job_data["command"], - "None", + job_data["name"], job_data["enabled"], job_data["one_time"], job_data["cron_string"], @@ -296,9 +318,10 @@ class TasksManager: # Check to see if it's enabled and is not a chain reaction. if job_data["enabled"] and job_data["interval_type"] != "reaction": + new_job = "error" if job_data["cron_string"] != "": try: - self.scheduler.add_job( + new_job = self.scheduler.add_job( HelpersManagement.add_command, CronTrigger.from_crontab( job_data["cron_string"], timezone=str(self.tz) @@ -312,6 +335,7 @@ class TasksManager: ], ) except Exception as e: + new_job = "error" Console.error(f"Failed to schedule task with error: {e}.") Console.warning("Removing failed task from DB.") logger.error(f"Failed to schedule task with error: {e}.") @@ -320,7 +344,7 @@ class TasksManager: self.controller.management_helper.delete_scheduled_task(sch_id) else: if job_data["interval_type"] == "hours": - self.scheduler.add_job( + new_job = self.scheduler.add_job( HelpersManagement.add_command, "cron", minute=0, @@ -334,7 +358,7 @@ class TasksManager: ], ) elif job_data["interval_type"] == "minutes": - self.scheduler.add_job( + new_job = self.scheduler.add_job( HelpersManagement.add_command, "cron", minute="*/" + str(job_data["interval"]), @@ -348,7 +372,7 @@ class TasksManager: ) elif job_data["interval_type"] == "days": curr_time = job_data["start_time"].split(":") - self.scheduler.add_job( + new_job = self.scheduler.add_job( HelpersManagement.add_command, "cron", day="*/" + str(job_data["interval"]), @@ -364,6 +388,14 @@ class TasksManager: ) logger.info("Added job. Current enabled schedules: ") jobs = self.scheduler.get_jobs() + if new_job != "error": + task = self.controller.management.get_scheduled_task_model( + int(new_job.id) + ) + self.controller.management.update_scheduled_task( + task.schedule_id, + {"next_run": new_job.next_run_time.strftime("%m/%d/%Y, %H:%M:%S")}, + ) for item in jobs: logger.info(f"JOB: {item}") @@ -416,9 +448,10 @@ class TasksManager: ) if job_data["enabled"] and job_data["interval"] != "reaction": + new_job = "error" if job_data["cron_string"] != "": try: - self.scheduler.add_job( + new_job = self.scheduler.add_job( HelpersManagement.add_command, CronTrigger.from_crontab( job_data["cron_string"], timezone=str(self.tz) @@ -432,12 +465,13 @@ class TasksManager: ], ) except Exception as e: + new_job = "error" Console.error(f"Failed to schedule task with error: {e}.") Console.info("Removing failed task from DB.") self.controller.management_helper.delete_scheduled_task(sch_id) else: if job_data["interval_type"] == "hours": - self.scheduler.add_job( + new_job = self.scheduler.add_job( HelpersManagement.add_command, "cron", minute=0, @@ -451,7 +485,7 @@ class TasksManager: ], ) elif job_data["interval_type"] == "minutes": - self.scheduler.add_job( + new_job = self.scheduler.add_job( HelpersManagement.add_command, "cron", minute="*/" + str(job_data["interval"]), @@ -465,7 +499,7 @@ class TasksManager: ) elif job_data["interval_type"] == "days": curr_time = job_data["start_time"].split(":") - self.scheduler.add_job( + new_job = self.scheduler.add_job( HelpersManagement.add_command, "cron", day="*/" + str(job_data["interval"]), @@ -479,6 +513,14 @@ class TasksManager: job_data["command"], ], ) + if new_job != "error": + task = self.controller.management.get_scheduled_task_model( + int(new_job.id) + ) + self.controller.management.update_scheduled_task( + task.schedule_id, + {"next_run": new_job.next_run_time.strftime("%m/%d/%Y, %H:%M:%S")}, + ) else: try: self.scheduler.get_job(str(sch_id)) @@ -506,6 +548,15 @@ class TasksManager: if task.one_time: self.remove_job(task.schedule_id) logger.info("one time task detected. Deleting...") + else: + self.controller.management.update_scheduled_task( + task.schedule_id, + { + "next_run": self.scheduler.get_job( + event.job_id + ).next_run_time.strftime("%m/%d/%Y, %H:%M:%S") + }, + ) # check for any child tasks for this. It's kind of backward, # but this makes DB management a lot easier. One to one # instead of one to many. @@ -606,6 +657,16 @@ class TasksManager: ) time.sleep(1) + def check_for_updates(self): + logger.info("Checking for Crafty updates...") + self.helper.update_available = self.helper.check_remote_version() + if self.helper.update_available: + logger.info(f"Found new version {self.helper.update_available}") + else: + logger.info( + "No updates found! You are on the most up to date Crafty version." + ) + def log_watcher(self): self.controller.servers.check_for_old_logs() self.scheduler.add_job( diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py index 44ba6b34..9aa41d28 100644 --- a/app/classes/web/ajax_handler.py +++ b/app/classes/web/ajax_handler.py @@ -454,6 +454,14 @@ class AjaxHandler(BaseHandler): self.helper.backup_select(path, exec_user["user_id"]) return + elif page == "jar_cache": + if not superuser: + self.redirect("/panel/error?error=Not a super user") + return + + self.controller.server_jars.manual_refresh_cache() + return + @tornado.web.authenticated def delete(self, page): api_key, _, exec_user = self.current_user @@ -565,9 +573,39 @@ class AjaxHandler(BaseHandler): self.get_remote_ip(), ) + for server in self.controller.servers.failed_servers: + if server["server_id"] == int(server_id): + return self.tasks_manager.remove_all_server_tasks(server_id) self.controller.remove_server(server_id, True) + elif page == "delete_unloaded_server": + if not permissions["Config"] in user_perms: + if not superuser: + self.redirect("/panel/error?error=Unauthorized access to Config") + return + server_id = self.get_argument("id", None) + logger.info( + f"Removing server and all associated files for server: " + f"{self.controller.servers.get_server_friendly_name(server_id)}" + ) + + server_data = self.controller.servers.get_server_data_by_id(server_id) + server_name = server_data["server_name"] + + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Deleted server {server_id} named {server_name}", + server_id, + self.get_remote_ip(), + ) + + self.tasks_manager.remove_all_server_tasks(server_id) + for item in self.controller.servers.failed_servers[:]: + if item["server_id"] == int(server_id): + self.controller.servers.failed_servers.remove(item) + self.controller.remove_unloaded_server(server_id) + def check_server_id(self, server_id, page_name): if server_id is None: logger.warning( diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index d592f627..2334c862 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -157,6 +157,10 @@ class PanelHandler(BaseHandler): if server_id is None: self.redirect("/panel/error?error=Invalid Server ID") return None + for server in self.controller.servers.failed_servers: + if int(server_id) == server["server_id"]: + self.failed_server = True + return server_id # Does this server exist? if not self.controller.servers.server_id_exists(server_id): self.redirect("/panel/error?error=Invalid Server ID") @@ -207,6 +211,7 @@ class PanelHandler(BaseHandler): @tornado.web.authenticated async def get(self, page): + self.failed_server = False error = self.get_argument("error", "WTF Error!") template = "panel/denied.html" @@ -284,9 +289,10 @@ class PanelHandler(BaseHandler): page_data: t.Dict[str, t.Any] = { # todo: make this actually pull and compare version data - "update_available": False, + "update_available": self.helper.update_available, "serverTZ": tz, "version_data": self.helper.get_version_string(), + "failed_servers": self.controller.servers.failed_servers, "user_data": exec_user, "user_role": exec_user_role, "user_crafty_permissions": exec_user_crafty_permissions, @@ -368,7 +374,7 @@ class PanelHandler(BaseHandler): ) as credits_default_local: try: remote = requests.get( - "https://craftycontrol.com/credits", allow_redirects=True + "https://craftycontrol.com/credits-v2", allow_redirects=True ) credits_dict: dict = remote.json() if not credits_dict["staff"]: @@ -503,9 +509,11 @@ class PanelHandler(BaseHandler): server_id = self.check_server_id() if server_id is None: return - - server_obj = self.controller.servers.get_server_instance_by_id(server_id) - page_data["backup_failed"] = server_obj.last_backup_status() + if not self.failed_server: + server_obj = self.controller.servers.get_server_instance_by_id( + server_id + ) + page_data["backup_failed"] = server_obj.last_backup_status() server_obj = None valid_subpages = [ @@ -517,18 +525,57 @@ class PanelHandler(BaseHandler): "admin_controls", "schedules", ] - - server = self.controller.servers.get_server_instance_by_id(server_id) + if not self.failed_server: + server = self.controller.servers.get_server_instance_by_id(server_id) # server_data isn't needed since the server_stats also pulls server data page_data["server_data"] = self.controller.servers.get_server_data_by_id( server_id ) - page_data["server_stats"] = self.controller.servers.get_server_stats_by_id( - server_id - ) - page_data["downloading"] = self.controller.servers.get_download_status( - server_id - ) + if not self.failed_server: + page_data[ + "server_stats" + ] = self.controller.servers.get_server_stats_by_id(server_id) + else: + server_temp_obj = self.controller.servers.get_server_data_by_id( + server_id + ) + page_data["server_stats"] = { + "server_id": { + "server_id": server_id, + "server_name": server_temp_obj["server_name"], + "server_uuid": server_temp_obj["server_uuid"], + "path": server_temp_obj["path"], + "log_path": server_temp_obj["log_path"], + "executable": server_temp_obj["executable"], + "execution_command": server_temp_obj["execution_command"], + "stop_command": server_temp_obj["stop_command"], + "executable_update_url": server_temp_obj[ + "executable_update_url" + ], + "auto_start_delay": server_temp_obj["auto_start_delay"], + "server_ip": server_temp_obj["server_ip"], + "server_port": server_temp_obj["server_port"], + "logs_delete_after": server_temp_obj["logs_delete_after"], + "auto_start": server_temp_obj["auto_start"], + "crash_detection": server_temp_obj["crash_detection"], + "show_status": server_temp_obj["show_status"], + }, + "running": False, + "crashed": False, + "server_type": "N/A", + "cpu": "N/A", + "mem": "N/A", + "int_ping_results": [], + "version": "N/A", + "desc": "N/A", + "started": "False", + } + if not self.failed_server: + page_data["downloading"] = self.controller.servers.get_download_status( + server_id + ) + else: + page_data["downloading"] = False page_data["server_id"] = server_id try: page_data["waiting_start"] = self.controller.servers.get_waiting_start( @@ -537,7 +584,10 @@ class PanelHandler(BaseHandler): except Exception as e: logger.error(f"Failed to get server waiting to start: {e}") page_data["waiting_start"] = False - page_data["get_players"] = server.get_server_players() + if not self.failed_server: + page_data["get_players"] = server.get_server_players() + else: + page_data["get_players"] = [] page_data["active_link"] = subpage page_data["permissions"] = { "Commands": EnumPermissionsServer.COMMANDS, @@ -554,12 +604,14 @@ class PanelHandler(BaseHandler): ] = self.controller.server_perms.get_user_id_permissions_list( exec_user["user_id"], server_id ) - page_data["server_stats"]["crashed"] = self.controller.servers.is_crashed( - server_id - ) - page_data["server_stats"][ - "server_type" - ] = self.controller.servers.get_server_type_by_id(server_id) + if not self.failed_server: + page_data["server_stats"][ + "crashed" + ] = self.controller.servers.is_crashed(server_id) + if not self.failed_server: + page_data["server_stats"][ + "server_type" + ] = self.controller.servers.get_server_type_by_id(server_id) if subpage not in valid_subpages: logger.debug("not a valid subpage") if not subpage: @@ -639,6 +691,7 @@ class PanelHandler(BaseHandler): return page_data["java_versions"] = Helpers.find_java_installs() server_obj: Servers = self.controller.servers.get_server_obj(server_id) + page_data["failed"] = self.failed_server page_java = [] page_data["java_versions"].append("java") for version in page_data["java_versions"]: @@ -901,6 +954,7 @@ class PanelHandler(BaseHandler): page_data["new_schedule"] = True page_data["schedule"] = {} page_data["schedule"]["children"] = [] + page_data["schedule"]["name"] = "" page_data["schedule"]["server_id"] = server_id page_data["schedule"]["schedule_id"] = "" page_data["schedule"]["action"] = "" @@ -968,6 +1022,10 @@ class PanelHandler(BaseHandler): page_data["schedule"]["server_id"] = server_id page_data["schedule"]["schedule_id"] = schedule.schedule_id page_data["schedule"]["action"] = schedule.action + if schedule.name: + page_data["schedule"]["name"] = schedule.name + else: + page_data["schedule"]["name"] = "" page_data["schedule"][ "children" ] = self.controller.management.get_child_schedules(sch_id) @@ -1466,7 +1524,15 @@ class PanelHandler(BaseHandler): server_obj.auto_start = auto_start server_obj.crash_detection = crash_detection server_obj.logs_delete_after = logs_delete_after - self.controller.servers.update_server(server_obj) + failed = False + for servers in self.controller.servers.failed_servers: + if servers["server_id"] == int(server_id): + failed = True + if not failed: + self.controller.servers.update_server(server_obj) + else: + self.controller.servers.update_unloaded_server(server_obj) + self.controller.servers.init_all_servers() self.controller.servers.crash_detection(server_obj) self.controller.servers.refresh_server_settings(server_id) @@ -1557,6 +1623,7 @@ class PanelHandler(BaseHandler): difficulty = bleach.clean(self.get_argument("difficulty", None)) server_obj = self.controller.servers.get_server_obj(server_id) enabled = bleach.clean(self.get_argument("enabled", "0")) + name = bleach.clean(self.get_argument("name", "")) if difficulty == "basic": action = bleach.clean(self.get_argument("action", None)) interval = bleach.clean(self.get_argument("interval", None)) @@ -1621,6 +1688,7 @@ class PanelHandler(BaseHandler): if interval_type == "days": job_data = { + "name": name, "server_id": server_id, "action": action, "interval_type": interval_type, @@ -1635,6 +1703,7 @@ class PanelHandler(BaseHandler): } elif difficulty == "reaction": job_data = { + "name": name, "server_id": server_id, "action": action, "interval_type": interval_type, @@ -1650,6 +1719,7 @@ class PanelHandler(BaseHandler): } elif difficulty == "advanced": job_data = { + "name": name, "server_id": server_id, "action": action, "interval_type": "", @@ -1665,6 +1735,7 @@ class PanelHandler(BaseHandler): } else: job_data = { + "name": name, "server_id": server_id, "action": action, "interval_type": interval_type, @@ -1714,6 +1785,7 @@ class PanelHandler(BaseHandler): difficulty = bleach.clean(self.get_argument("difficulty", None)) server_obj = self.controller.servers.get_server_obj(server_id) enabled = bleach.clean(self.get_argument("enabled", "0")) + name = bleach.clean(self.get_argument("name", "")) if difficulty == "basic": action = bleach.clean(self.get_argument("action", None)) interval = bleach.clean(self.get_argument("interval", None)) @@ -1777,6 +1849,7 @@ class PanelHandler(BaseHandler): if interval_type == "days": job_data = { + "name": name, "server_id": server_id, "action": action, "interval_type": interval_type, @@ -1791,6 +1864,7 @@ class PanelHandler(BaseHandler): } elif difficulty == "advanced": job_data = { + "name": name, "server_id": server_id, "action": action, "interval_type": "", @@ -1806,6 +1880,7 @@ class PanelHandler(BaseHandler): } elif difficulty == "reaction": job_data = { + "name": name, "server_id": server_id, "action": action, "interval_type": interval_type, @@ -1821,6 +1896,7 @@ class PanelHandler(BaseHandler): } else: job_data = { + "name": name, "server_id": server_id, "action": action, "interval_type": interval_type, diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index e4e10a04..df4ba684 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -92,10 +92,12 @@ class ServerHandler(BaseHandler): template = "public/404.html" page_data = { + "update_available": self.helper.update_available, "version_data": self.helper.get_version_string(), "user_data": exec_user, "user_role": exec_user_role, "roles": list_roles, + "super_user": exec_user["superuser"], "user_crafty_permissions": exec_user_crafty_permissions, "crafty_permissions": { "Server_Creation": EnumPermissionsCrafty.SERVER_CREATION, @@ -386,14 +388,20 @@ class ServerHandler(BaseHandler): # deletes temp dir FileHelpers.del_dirs(zip_path) else: - if len(server_parts) != 2: + if len(server_parts) != 3: self.redirect("/panel/error?error=Invalid server data") return - server_type, server_version = server_parts + jar_type, server_type, server_version = server_parts # TODO: add server type check here and call the correct server # add functions if not a jar new_server_id = self.controller.create_jar_server( - server_type, server_version, server_name, min_mem, max_mem, port + jar_type, + server_type, + server_version, + server_name, + min_mem, + max_mem, + port, ) self.controller.management.add_to_audit_log( exec_user["user_id"], diff --git a/app/config/credits.json b/app/config/credits.json index 9417c784..ae2017d9 100644 --- a/app/config/credits.json +++ b/app/config/credits.json @@ -1,139 +1,238 @@ { "staff": { - "development": [ - { - "name": "Pita Bread", - "title": "CEO", - "loc": "Midwest, USA", - "tags": [ "Staff", [ "Developer", "https://gitlab.com/craftbreadth" ], "Crafty Leadership" ], - "blurb": "Baked goods enthusiast who dabbles in Linux and sets up networks for fun. Can be found writing long-winded emails, debugging strange wifi issues, or taking care of his extensive houseplant collection.", - "pic": "/static/assets/images/credits/pita.png" - }, - { - "name": "Xithical", - "title": "CFO/COO", - "loc": "Midwest, USA", - "tags": [ "Staff", [ "Developer", "https://gitlab.com/xithical" ], "Crafty Leadership" ], - "blurb": "Having sold his soul to the IT administration gods many eons ago, you can usually find him working, going home to do work, or continuing to work in the support Discord.", - "pic": "/static/assets/images/credits/xithical.png" - }, - { - "name": "Phil Tarrant", - "title": "Creator", - "loc": "Southeast, USA", - "tags": [ "Staff", [ "Developer", "https://gitlab.com/Ptarrant1" ], "Crafty Advisor" ], - "blurb": "For best results, apply a thin layer of Phillip to code, cyber security, and parenthood for maximum effectiveness. Phillip often spends too much time with his chickens.", - "pic": "/static/assets/images/credits/ptarrant.png" - }, - { - "name": "macgeek", - "title": "Lead Software Engineer", - "loc": "Midwest, USA", - "tags": [ "Staff", [ "Developer", "https://gitlab.com/computergeek125" ], "Crafty Advisor" ], - "blurb": "Sysadmin for work and sysadmin for fun (avid homelabber). He enjoys a good tech tangent and gaming.", - "pic": "/static/assets/images/credits/macgeek.png" - }, - { - "name": "parzivaldewey", - "title": "Support Manager", - "loc": "East Coast, USA", - "tags": [ "Staff", [ "Developer", "https://gitlab.com/amcmanu3" ], "Support Manager" ], - "blurb": "His interests include Linux, gaming, and helping others. When he's able to unplug he enjoys biking, hiking, and playing soccer.", - "pic": "/static/assets/images/credits/parzivaldewey.png" - }, - { - "name": "Silversthorn", - "title": "Software Engineer", - "loc": "Provence, FR", - "tags": [ "Staff", [ "Developer", "https://gitlab.com/Silversthorn" ], null ], - "blurb": "Developer at work and at home, testing his own code is a pain, so his coding precept is \"Testing is Doubting\"", - "pic": "/static/assets/images/credits/silversthorn.png" - }, - { - "name": "ThatOneLukas", - "title": "Software Engineer", - "loc": "Helsinki, FI", - "tags": [ "Staff", [ "Developer", "https://gitlab.com/LukasDoesDev" ], null ], - "blurb": "Arch Linux enthusiast who likes 80s/90s music, Rust (The programming language) and light distros like Arch Linux btw. Dislikes C, C++, Go, and Microsoft. Also doesn't like Finnish weather that freezes molten snow to (deadly) ice.", - "pic": "/static/assets/images/credits/lukas.png" - }, - { - "name": "Zedifus", - "title": "DevOps Engineer", - "loc": "Scotland, UK", - "tags": [ "Staff", [ "Developer", "https://gitlab.com/Zedifus" ], "DevOps" ], - "blurb": "A Streamer, who is currently working in the insurance industry, self-teaching software development & DevOps, with the hopes of a career change! Enjoys playing games and has a sassy cat called Eve.", - "pic": "/static/assets/images/credits/zedifus.jpg" - } - ], - "support": [ - { - "name": "iSilverfyre", - "title": "Community Manager", - "loc": "South Central, US", - "tags": [ "Staff", null, "Wiki" ], - "blurb": "Silver enjoys helping others with their computer needs, writing documentation, and loving her cat.", - "pic": "/static/assets/images/credits/isilverfyre.png" - }, - { - "name": "Quentin", - "title": "Document Curator", - "loc": null, - "tags": [ "Staff", [ "Developer", "https://gitlab.com/qub3d" ], "Wiki" ], - "blurb": "Hosts Minecraft servers for his weird friends, works for an IoT company as his dayjob. The 's' in IoT stands for 'secure'.", - "pic": "/static/assets/images/credits/qub3d.png" - }, - { - "name": "DarthLeo", - "title": "Support Engineer", - "loc": "East Coast, US", - "tags": [ "Staff", null, "Discord" ], - "blurb": "Just a simple gamer.", - "pic": "/static/assets/images/credits/darthLeo.png" - } - ], - "retired": [ - { - "name": "Kev Dagoat", - "title": "Head of Development", - "loc": "East Coast, AU", - "tags": [ "Staff", [ "Developer", "https://gitlab.com/kevdagoat" ], "HOD" ], - "blurb": "His interests include Linux, programming, and goats of course. He enjoys building APIs, K8s, and geeking over video cards.", - "pic": "/static/assets/images/credits/kevdagoat.jpeg" - }, - { - "name": "Manu", - "title": null, - "loc": "Eastern, CA", - "tags": [ "Staff", "Developer", "Project Manager" ], - "blurb": "His interests include learning, Linux, and programming. He enjoys speaking French, doing 6 things at once, and testing software.", - "pic": "/static/assets/images/credits/manu.png" - }, - { - "name": "UltraBlack", - "title": null, - "loc": "Bavaria, DE", - "tags": [ "Staff", null, "Idea Manager" ], - "blurb": "Hi, my name is Tim, and I'm a huge fan of linux. I'm often gaming and occasionally coding.", - "pic": "/static/assets/images/credits/ultrablack.png" - }, - { - "name": "MC Gaming", - "title": null, - "loc": "Central, UK", - "tags": [ "Staff", [ "Developer", "https://gitlab.com/MCgamin1738" ], null ], - "blurb": "His interests include learning, Linux, and programming. He loves pentesting apps and gaming.", - "pic": "/static/assets/images/credits/mcgaming.png" - } - ] + "development": [ + { + "name": "Pita Bread", + "title": "CEO", + "loc": "Midwest, USA", + "tags": [ + "Staff", + [ + "Developer", + "https://gitlab.com/craftbreadth" + ], + "Crafty Leadership" + ], + "blurb": "Baked goods enthusiast who dabbles in Linux and sets up networks for fun. Can be found writing long-winded emails, debugging strange wifi issues, or taking care of his extensive houseplant collection.", + "pic": "/static/assets/images/credits/pita.png" + }, + { + "name": "Xithical", + "title": "CFO/COO", + "loc": "Midwest, USA", + "tags": [ + "Staff", + [ + "Developer", + "https://gitlab.com/xithical" + ], + "Crafty Leadership" + ], + "blurb": "Having sold his soul to the IT administration gods many eons ago, you can usually find him working, going home to do work, or continuing to work in the support Discord.", + "pic": "/static/assets/images/credits/xithical.png" + }, + { + "name": "Phil Tarrant", + "title": "Creator", + "loc": "Southeast, USA", + "tags": [ + "Staff", + [ + "Developer", + "https://gitlab.com/Ptarrant1" + ], + "Crafty Advisor" + ], + "blurb": "For best results, apply a thin layer of Phillip to code, cyber security, and parenthood for maximum effectiveness. Phillip often spends too much time with his chickens.", + "pic": "/static/assets/images/credits/ptarrant.png" + }, + { + "name": "macgeek", + "title": "Lead Software Engineer", + "loc": "Midwest, USA", + "tags": [ + "Staff", + [ + "Developer", + "https://gitlab.com/computergeek125" + ], + "Crafty Advisor" + ], + "blurb": "Sysadmin for work and sysadmin for fun (avid homelabber). He enjoys a good tech tangent and gaming.", + "pic": "/static/assets/images/credits/macgeek.png" + }, + { + "name": "pretzeldewey πŸ₯¨", + "title": "Support Manager & Software Engineer", + "loc": "East Coast, USA", + "tags": [ + "Staff", + [ + "Developer", + "https://gitlab.com/amcmanu3" + ], + "Crafty Advisor" + ], + "blurb": "His interests include Linux, gaming, and helping others. When he's able to unplug he enjoys biking, hiking, and playing soccer.", + "pic": "/static/assets/images/credits/parzivaldewey.png" + }, + { + "name": "Zedifus", + "title": "DevOps Manager", + "loc": "Scotland, UK", + "tags": [ + "Staff", + [ + "Developer", + "https://gitlab.com/Zedifus" + ], + "Crafty Advisor" + ], + "blurb": "A Streamer, who is currently working in the insurance industry, self-teaching software development & DevOps, with the hopes of a career change! Enjoys playing games and has a sassy cat called Eve.", + "pic": "/static/assets/images/credits/zedifus.jpg" + }, + { + "name": "Silversthorn", + "title": "Software Engineer", + "loc": "Provence, FR", + "tags": [ + "Staff", + [ + "Developer", + "https://gitlab.com/Silversthorn" + ], + null + ], + "blurb": "Developer at work and at home, testing his own code is a pain, so his coding precept is \"Testing is Doubting\"", + "pic": "/static/assets/images/credits/silversthorn.png" + }, + { + "name": "ThatOneLukas", + "title": "Software Engineer", + "loc": "Helsinki, FI", + "tags": [ + "Staff", + [ + "Developer", + "https://gitlab.com/LukasDoesDev" + ], + null + ], + "blurb": "Arch Linux enthusiast who likes 80s/90s music, Rust (The programming language) and light distros like Arch Linux btw. Dislikes C, C++, Go, and Microsoft. Also doesn't like Finnish weather that freezes molten snow to (deadly) ice.", + "pic": "/static/assets/images/credits/lukas.png" + } + ], + "support": [ + { + "name": "iSilverfyre", + "title": "Community Manager", + "loc": "South Central, US", + "tags": [ + "Staff", + null, + "Wiki" + ], + "blurb": "Silver enjoys helping others with their computer needs, writing documentation, and loving her cat.", + "pic": "/static/assets/images/credits/isilverfyre.png" + }, + { + "name": "Quentin", + "title": "Document Curator", + "loc": null, + "tags": [ + "Staff", + [ + "Developer", + "https://gitlab.com/qub3d" + ], + "Wiki" + ], + "blurb": "Hosts Minecraft servers for his weird friends, works for an IoT company as his dayjob. The 's' in IoT stands for 'secure'.", + "pic": "/static/assets/images/credits/qub3d.png" + }, + { + "name": "Kornster", + "title": null, + "loc": "Victoria, Australia", + "tags": [ + "Discord Mod", + null, + null + ], + "blurb": "Day to day I am a Network Administrator, dealing with a range of vendors and products. Outside of work my hobbies are handyman things, such as carpentry and motor mechanics having grown up around them. My home-lab consists of Linux distros ranging from Debian, Ubuntu & Centos having self taught myself Linux for the last 12+ years.", + "pic": null + } + ], + "retired": [ + { + "name": "Kev Dagoat", + "title": "Head of Development", + "loc": "East Coast, AU", + "tags": [ + "Staff", + [ + "Developer", + "https://gitlab.com/kevdagoat" + ], + "HOD" + ], + "blurb": "His interests include Linux, programming, and goats of course. He enjoys building APIs, K8s, and geeking over video cards.", + "pic": "/static/assets/images/credits/kevdagoat.jpeg" + }, + { + "name": "Manu", + "title": null, + "loc": "Eastern, CA", + "tags": [ + "Staff", + "Developer", + "Project Manager" + ], + "blurb": "His interests include learning, Linux, and programming. He enjoys speaking French, doing 6 things at once, and testing software.", + "pic": "/static/assets/images/credits/manu.png" + }, + { + "name": "UltraBlack", + "title": null, + "loc": "Bavaria, DE", + "tags": [ + "Staff", + null, + "Idea Manager" + ], + "blurb": "Hi, my name is Tim, and I'm a huge fan of linux. I'm often gaming and occasionally coding.", + "pic": "/static/assets/images/credits/ultrablack.png" + }, + { + "name": "MC Gaming", + "title": null, + "loc": "Central, UK", + "tags": [ + "Staff", + [ + "Developer", + "https://gitlab.com/MCgamin1738" + ], + null + ], + "blurb": "His interests include learning, Linux, and programming. He loves pentesting apps and gaming.", + "pic": "/static/assets/images/credits/mcgaming.png" + } + ] }, "translations": { - "Data Unavailable": ["🌎πŸͺ£πŸ”—πŸ‘ŽπŸ˜­"] + "Data Unavailable": [ + { + "name": "🌎πŸͺ£πŸ”—πŸ‘ŽπŸ˜­", + "status": false + } + ] }, "patrons": [ { "name": "Data Unavailable 🌎πŸͺ£πŸ”—πŸ‘ŽπŸ˜­", - "level": "" + "level": "", + "source": "" } ], "lastUpdate": 0 diff --git a/app/config/version.json b/app/config/version.json index 1d4ec0c1..a10889cc 100644 --- a/app/config/version.json +++ b/app/config/version.json @@ -1,6 +1,6 @@ { "major": 4, "minor": 0, - "sub": 8, + "sub": 10, "meta": "beta" } diff --git a/app/frontend/templates/footer.html b/app/frontend/templates/footer.html index fec76eba..65840cb8 100644 --- a/app/frontend/templates/footer.html +++ b/app/frontend/templates/footer.html @@ -1,12 +1,27 @@ diff --git a/app/frontend/templates/panel/credits.html b/app/frontend/templates/panel/credits.html index 89b3678e..f10507fa 100644 --- a/app/frontend/templates/panel/credits.html +++ b/app/frontend/templates/panel/credits.html @@ -342,7 +342,11 @@
{% for language in data['translations'][person] %} - {{ language }} + {% if language['status'] %} + {{ language['name'] }} + {% else %} + {{ language['name'] }} + {% end %} {% end %}
diff --git a/app/frontend/templates/panel/dashboard.html b/app/frontend/templates/panel/dashboard.html index 00aee7e1..ec5a21db 100644 --- a/app/frontend/templates/panel/dashboard.html +++ b/app/frontend/templates/panel/dashboard.html @@ -60,12 +60,12 @@

+ title="{% raw translate('dashboard', 'cpuCores', data['lang']) %}: {{ data.get('hosts_data').get('cpu_cores') }}
{% raw translate('dashboard', 'cpuCurFreq', data['lang']) %}: {{ data.get('hosts_data').get('cpu_cur_freq') }}
{% raw translate('dashboard', 'cpuMaxFreq', data['lang']) %}: {{ data.get('hosts_data').get('cpu_max_freq') }}"> {{ translate('dashboard', 'cpuUsage', data['lang']) }}: {{ data.get('hosts_data').get('cpu_usage') }}

+ title="{{ translate('dashboard', 'memUsage', data['lang']) }}: {{ data.get('hosts_data').get('mem_usage') }}"> {{ translate('dashboard', 'memUsage', data['lang']) }}: {{ data.get('hosts_data').get('mem_percent') }}%

@@ -116,8 +116,8 @@ {% if len(data['servers']) > 0 %} {% if data['user_data']['hints'] %} + data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" , + data-placement="top"> {% end %} {% end %}
  {{ @@ -125,7 +125,7 @@
- {% if len(data['servers']) == 0%} + {% if len(data['servers']) == 0 and len(data['failed_servers']) == 0 %}

{{ translate('dashboard', 'welcome', data['lang']) }}


@@ -134,7 +134,7 @@
{% end %} - {% if len(data['servers']) > 0 %} + {% if len(data['servers']) > 0 or len(data['failed_servers']) > 0 %}
@@ -156,7 +156,7 @@ {% if server['alert'] %} + href="/panel/server_detail?id={{server['server_data']['server_id']}}"> {{ server['server_data']['server_name'] }}  {% else %} @@ -170,17 +170,17 @@ {% if server['user_command_permission'] %} {% if server['stats']['running'] %} + title="{{ translate('dashboard', 'stop' , data['lang']) }}">   + title="{{ translate('dashboard', 'restart' , data['lang']) }}">   + title="{{ translate('dashboard', 'kill' , data['lang']) }}">   @@ -199,15 +199,15 @@ data['lang']) }} {% else %} + title="{{ translate('dashboard', 'start' , data['lang']) }}">   + title="{{ translate('dashboard', 'clone' , data['lang']) }}">   + title="{{ translate('dashboard', 'kill' , data['lang']) }}">   {% end %} @@ -216,7 +216,7 @@ + data-players="{{ server['stats']['online']}}" data-max="{{ server['stats']['max'] }}"> + + {% end %} + {% for server in data['failed_servers'] %} + + + + + + + + {% end %} @@ -306,27 +319,27 @@
+ class="btn btn-link d-flex justify-content-start" type="button"> {% if server['stats']['running'] %} {{ translate('dashboard', 'online', data['lang']) }} @@ -348,22 +361,22 @@
+ aria-labelledby="heading-{{server['server_data']['server_id']}}" data-parent="#accordionServers">
{{ translate('dashboard', 'cpuUsage', data['lang']) }}
+ title="{{server['stats']['cpu']}}">
+ aria-valuemax="100">
{{server['stats']['cpu']}}%
@@ -452,7 +465,7 @@
{{ translate('dashboard', 'memUsage', data['lang']) }}
+ title="{{server['stats']['mem']}}">
+ aria-valuemin="0" aria-valuemax="100">
{{server['stats']['mem_percent']}}% - @@ -492,7 +505,7 @@ {% if server['stats']['desc'] != 'False' %}
+ style="overflow-wrap: break-word !important; max-width: 85px !important; overflow: scroll;"> {{ server['stats']['desc'] }}

{% end %} diff --git a/app/frontend/templates/panel/panel_edit_user_apikeys.html b/app/frontend/templates/panel/panel_edit_user_apikeys.html index d0d650b5..062cf8d9 100644 --- a/app/frontend/templates/panel/panel_edit_user_apikeys.html +++ b/app/frontend/templates/panel/panel_edit_user_apikeys.html @@ -15,7 +15,7 @@ @@ -32,14 +32,12 @@ @@ -48,56 +46,60 @@
-

{{ translate('apiKeys', 'apiKeys', data['lang']) }}

+

{{ translate('apiKeys', 'apiKeys', + data['lang']) }}

+ title="{{server['stats']['cpu']}}">
+ aria-valuemin="0" aria-valuemax="100">
{{server['stats']['cpu']}}%
+ title="{{server['stats']['mem']}}">
+ aria-valuemin="0" aria-valuemax="100">
{{server['stats']['mem_percent']}}% - @@ -263,7 +263,7 @@ {% if server['stats']['desc'] != 'False' %}
{{ + style="overflow-wrap: break-word !important; max-width: 85px !important; overflow: scroll;">{{ server['stats']['desc'] }}

{% end %} @@ -287,7 +287,20 @@ {% end %}
 {{server['server_name']}} +  Unloaded
- - - - - - - - + + + + + + + + - {% for apikey in data['api_keys'] %} - - - - - - - + + + + - - {% end %} + data-key-name="{{ apikey.name }}">{{ + translate('apiKeys', 'getToken', data['lang']) }} + + + + {% end %}
{{ translate('apiKeys', 'name', data['lang']) }}{{ translate('apiKeys', 'created', data['lang']) }}{{ translate('apiKeys', 'superUser', data['lang']) }}{{ translate('apiKeys', 'perms', data['lang']) }}{{ translate('apiKeys', 'buttons', data['lang']) }}
{{ translate('apiKeys', 'name', data['lang']) }}{{ translate('apiKeys', 'created', data['lang']) }}{{ translate('apiKeys', 'superUser', data['lang']) }}{{ translate('apiKeys', 'perms', data['lang']) }}{{ translate('apiKeys', 'buttons', data['lang']) }}
{{ apikey.name }}{{ apikey.created.strftime('%d/%m/%Y %H:%M:%S') }} - {% if apikey.superuser %} - - {{ translate('apiKeys', 'yes', data['lang']) }} - - {% else %} - - {{ translate('apiKeys', 'no', data['lang']) }} - - {% end %} - {{ translate('apiKeys', 'server', data['lang']) }} {{ apikey.server_permissions }} - {{ translate('apiKeys', 'crafty', data['lang']) }} {{ apikey.crafty_permissions }} - {{ apikey.name }}{{ apikey.created.strftime('%d/%m/%Y %H:%M:%S') }} + {% if apikey.superuser %} + + {{ + translate('apiKeys', 'yes', data['lang']) }} + + {% else %} + + {{ + translate('apiKeys', 'no', data['lang']) }} + + {% end %} + {{ translate('apiKeys', 'server', data['lang']) }} {{ + apikey.server_permissions }} + {{ translate('apiKeys', 'crafty', data['lang']) }} {{ + apikey.crafty_permissions }} + - + -
@@ -109,68 +111,69 @@
-

{{ translate('apiKeys', 'createNew', data['lang']) }}

+

{{ translate('apiKeys', + 'createNew', data['lang']) }}

+ action="/panel/edit_user_apikeys"> {% raw xsrf_form_html() %}
- + + placeholder="API Key">
- - - - + + + + - {% for permission in data['server_permissions_all'] %} - - - - - {% end %} - {% for permission in data['crafty_permissions_all'] %} - - - - - {% end %} + {% for permission in data['server_permissions_all'] %} + + + + + {% end %} + {% for permission in data['crafty_permissions_all'] %} + + + + + {% end %}
{{ translate('apiKeys', 'permName', data['lang']) }}{{ translate('apiKeys', 'auth', data['lang']) }}
{{ translate('apiKeys', 'permName', data['lang']) }}{{ translate('apiKeys', 'auth', data['lang']) }}
- - -
- - -
+ + +
+ + +
- + -
+
-
@@ -216,15 +219,17 @@ } }, callback: function (result) { - var token = getCookie("_xsrf") - $.ajax({ - type: "DELETE", - headers: {'X-XSRFToken': token}, - url: '/panel/remove_apikey?id=' + keyId, - success: function (data) { - location.reload(); - }, - }); + if (result) { + var token = getCookie("_xsrf") + $.ajax({ + type: "DELETE", + headers: { 'X-XSRFToken': token }, + url: '/panel/remove_apikey?id=' + keyId, + success: function (data) { + location.reload(); + }, + }); + } } }); }) @@ -234,7 +239,7 @@ var token = getCookie("_xsrf") $.ajax({ type: "POST", - headers: {'X-XSRFToken': token}, + headers: { 'X-XSRFToken': token }, url: '/panel/get_token?id=' + keyId, success: function (data) { bootbox.alert({ @@ -249,4 +254,4 @@ -{% end %} +{% end %} \ No newline at end of file diff --git a/app/frontend/templates/panel/server_config.html b/app/frontend/templates/panel/server_config.html index 9bb03a71..6c9a8b36 100644 --- a/app/frontend/templates/panel/server_config.html +++ b/app/frontend/templates/panel/server_config.html @@ -32,12 +32,14 @@
+ {% if not data['failed'] %} {% include "parts/server_controls_list.html %} {% include "parts/m_server_controls_list.html %} + {% end %}
@@ -117,7 +119,6 @@

{% end %} -
@@ -255,19 +269,19 @@
@@ -275,7 +289,7 @@ {% block js %} diff --git a/app/frontend/templates/panel/server_schedule_edit.html b/app/frontend/templates/panel/server_schedule_edit.html index ae4e54e3..34057466 100644 --- a/app/frontend/templates/panel/server_schedule_edit.html +++ b/app/frontend/templates/panel/server_schedule_edit.html @@ -9,14 +9,15 @@
- +
@@ -24,159 +25,212 @@
- {% include "parts/details_stats.html" %} + {% include "parts/details_stats.html" %}
- {% include "parts/server_controls_list.html" %} + {% include "parts/server_controls_list.html" %} -
-
- {% if data['new_schedule'] == True %} -
- {% else %} - +
+
+ {% if data['new_schedule'] == True %} + + {% else %} + {% end %} - {% raw xsrf_form_html() %} - - + {% raw xsrf_form_html() %} + + -
-
- -
-
-
- -
-
-
- - -
-
- -
-
-
- - -
-
-
- +
+

{{ translate('serverScheduleConfig', 'children' , data['lang']) }}

+ +
+
+ + +
+ + {% end %} - -
- - -{% end %} - -{% block js %} - + window.onload(startup()) + -{% end %} + {% end %} \ No newline at end of file diff --git a/app/frontend/templates/panel/server_schedules.html b/app/frontend/templates/panel/server_schedules.html index 2d965ad7..a64bb1ad 100644 --- a/app/frontend/templates/panel/server_schedules.html +++ b/app/frontend/templates/panel/server_schedules.html @@ -14,7 +14,8 @@