diff --git a/CHANGELOG.md b/CHANGELOG.md index 39681811..7e462a61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## --- [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 +- 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)) +<br><br> + ## --- [4.0.7] - 2022/07/18 ### New features - Task toggle ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/398)) diff --git a/README.md b/README.md index 40fd9026..021d6f3f 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ [](https://github.com/psf/black) [](https://www.python.org) -[](https://gitlab.com/crafty-controller/crafty-4/-/releases) +[](https://gitlab.com/crafty-controller/crafty-4/-/releases) [](https://gitlab.com/crafty-controller/crafty-4) [](https://gitlab.com/crafty-controller/crafty-4/-/commits/master) -# Crafty Controller 4.0.7-beta +# Crafty Controller 4.0.8-beta > Python based Control Panel for your Minecraft Server ## What is Crafty Controller? 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..c3eed588 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -115,6 +115,7 @@ class Schedules(BaseModel): cron_string = CharField(default="") parent = IntegerField(null=True) delay = IntegerField(default=0) + next_run = CharField(default="") class Meta: table_name = "schedules" @@ -285,6 +286,7 @@ class HelpersManagement: 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..393aa475 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 diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 2391b827..008fe989 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -247,11 +247,20 @@ class ServerInstance: "Oracle Java detected. Changing start command to avoid re-exec." ) which_java_raw = self.helper.which_java() - java_path = which_java_raw + "\\bin\\java" + try: + java_path = which_java_raw + "\\bin\\java" + except TypeError: + logger.warning( + "Could not find java in the registry even though" + " Oracle java is installed. Re-exec expected, but we have no" + " other options. CPU stats will not work for process." + ) + java_path = "" if str(which_java_raw) != str(self.helper.get_servers_root_dir) or str( self.helper.get_servers_root_dir ) in str(which_java_raw): - self.server_command[0] = java_path + if java_path != "": + self.server_command[0] = java_path else: logger.critcal( "Possible attack detected. User attempted to exec " @@ -647,6 +656,13 @@ class ServerInstance: self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {}) def restart_threaded_server(self, user_id): + bu_conf = HelpersManagement.get_backup_config(self.server_id) + if self.is_backingup and bu_conf["shutdown"]: + logger.info( + "Restart command detected. Supressing - server has" + " backup shutdown enabled and server is currently backing up." + ) + return # if not already running, let's just start if not self.check_running(): self.run_threaded_server(user_id) diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index 9876abc2..2e2540ea 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" # ) @@ -201,7 +210,7 @@ class TasksManager: if schedule.interval != "reaction": 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 +224,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 +235,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 +249,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 +263,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 +277,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: @@ -298,7 +319,7 @@ class TasksManager: if job_data["enabled"] and job_data["interval_type"] != "reaction": 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 +333,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 +342,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 +356,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 +370,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 +386,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}") @@ -418,7 +448,7 @@ class TasksManager: if job_data["enabled"] and job_data["interval"] != "reaction": 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 +462,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 +482,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 +496,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 +510,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 +545,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 +654,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..42dc4275 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 diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index fd4c2367..b5a07eb3 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -284,7 +284,7 @@ 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(), "user_data": exec_user, @@ -1878,15 +1878,12 @@ class PanelHandler(BaseHandler): # We don't want that. Automatically make them stay super user # since we know they are. if str(exec_user["user_id"]) != str(user_id): - superuser = bleach.clean(self.get_argument("superuser", "0")) + superuser = int(bleach.clean(self.get_argument("superuser", "0"))) else: - superuser = "1" + superuser = 1 else: - superuser = "0" - if superuser == "1": - superuser = True - else: - superuser = False + superuser = 0 + if not exec_user["superuser"]: if username is None or username == "": self.redirect("/panel/error?error=Invalid username") @@ -2062,13 +2059,9 @@ class PanelHandler(BaseHandler): ) # We don't want a non-super user to be able to create a super user. if superuser: - new_superuser = bleach.clean(self.get_argument("superuser", "0")) + new_superuser = int(bleach.clean(self.get_argument("superuser", "0"))) else: - new_superuser = "0" - if superuser == "1": - new_superuser = True - else: - new_superuser = False + new_superuser = 0 if EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions: self.redirect( 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/version.json b/app/config/version.json index 1ae57135..1d4ec0c1 100644 --- a/app/config/version.json +++ b/app/config/version.json @@ -1,6 +1,6 @@ { "major": 4, "minor": 0, - "sub": 7, + "sub": 8, "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 @@ <!-- partial:partials/_footer.html --> <footer class="footer"> <div class="container-fluid "> - -<span class="text-muted d-block text-center text-sm-left d-sm-inline-block">{{ translate('footer', 'copyright', data['lang']) }} © 2021 - <span x-data x-text="new Date().getFullYear()"></span> <a href="https://craftycontrol.com/" target="_blank">Crafty Controller</a>. {{ translate('footer', 'allRightsReserved', data['lang']) }}.</span> - -<span class="float-none float-sm-right d-block mt-1 mt-sm-0 text-center">{{ translate('footer', 'version', data['lang']) }}: {{ data['version_data'] }} - </span> + <span class="text-muted d-block text-center text-sm-left d-sm-inline-block">{{ translate('footer', 'copyright', data['lang']) }} © 2021 - <span x-data x-text="new Date().getFullYear()"></span> <a href="https://craftycontrol.com/" target="_blank">Crafty Controller</a>. {{ translate('footer', 'allRightsReserved', data['lang']) }}.</span> + <span class="float-none float-sm-right d-block mt-1 mt-sm-0"> {{ translate('footer', 'version', data['lang']) }}: {{ data['version_data'] }}</span> + {% if data['update_available'] %} + <span class="float-none float-sm-right d-block mt-1 mt-sm-0"><a target="_blank" class="blink-text" href="https://gitlab.com/crafty-controller/crafty-4/-/releases">Update Available!</a></span> + {% end %} </div> - + <style> + a:hover { + text-decoration: none; + } + .blink-text{ + color: #000; + font-weight: bold; + font-size: 2rem; + animation: blinkingText 2s infinite; + } + @keyframes blinkingText{ + 0% { color: grey;} + 50% { color: red;} + 100% { color: grey;} + } + </style> </footer> <!-- partial --> 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 @@ <div class="page-header"> <h4 class="page-title"> {{ translate('apiKeys', 'pageTitle', data['lang']) }} - {{ data['user']['user_id'] }} - <br/> + <br /> <small>UID: {{ data['user']['user_id'] }}</small> </h4> </div> @@ -32,14 +32,12 @@ <ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist"> <li class="nav-item"> <a class="nav-link" href="/panel/edit_user?id={{ data['user']['user_id'] }}&subpage=config" - role="tab" - aria-selected="false"> + role="tab" aria-selected="false"> <i class="fas fa-cogs"></i>{{ translate('apiKeys', 'config', data['lang']) }}</a> </li> <li class="nav-item"> <a class="nav-link active" href="/panel/edit_user_apikeys?id={{ data['user']['user_id'] }}" - role="tab" - aria-selected="true"> + role="tab" aria-selected="true"> <i class="fas fa-key"></i>{{ translate('apiKeys', 'apiKeys', data['lang']) }}</a> </li> </ul> @@ -48,56 +46,60 @@ <div class="col-md-7 col-sm-12"> <div class="card"> <div class="card-header header-sm d-flex justify-content-between align-items-center"> - <h4 class="card-title"><i class="fas fa-key"></i>{{ translate('apiKeys', 'apiKeys', data['lang']) }}</h4> + <h4 class="card-title"><i class="fas fa-key"></i>{{ translate('apiKeys', 'apiKeys', + data['lang']) }}</h4> </div> <div class="card-body"> <div class="form-group"> <div class="table-responsive"> <table class="table table-hover"> <thead> - <tr class="rounded"> - <!--<th>ID</th>--> - <th>{{ translate('apiKeys', 'name', data['lang']) }}</th> - <th>{{ translate('apiKeys', 'created', data['lang']) }}</th> - <th>{{ translate('apiKeys', 'superUser', data['lang']) }}</th> - <th>{{ translate('apiKeys', 'perms', data['lang']) }}</th> - <th>{{ translate('apiKeys', 'buttons', data['lang']) }}</th> - </tr> + <tr class="rounded"> + <!--<th>ID</th>--> + <th>{{ translate('apiKeys', 'name', data['lang']) }}</th> + <th>{{ translate('apiKeys', 'created', data['lang']) }}</th> + <th>{{ translate('apiKeys', 'superUser', data['lang']) }}</th> + <th>{{ translate('apiKeys', 'perms', data['lang']) }}</th> + <th>{{ translate('apiKeys', 'buttons', data['lang']) }}</th> + </tr> </thead> <tbody> - {% for apikey in data['api_keys'] %} - <tr> - <!--<td>{-{ apikey.token_id }-}</td>--> - <td>{{ apikey.name }}</td> - <td>{{ apikey.created.strftime('%d/%m/%Y %H:%M:%S') }}</td> - <td> - {% if apikey.superuser %} - <span class="text-success"> - <i class="fas fa-check-square"></i> {{ translate('apiKeys', 'yes', data['lang']) }} - </span> - {% else %} - <span class="text-danger"> - <i class="far fa-times-square"></i> {{ translate('apiKeys', 'no', data['lang']) }} - </span> - {% end %} - </td> - <td>{{ translate('apiKeys', 'server', data['lang']) }} {{ apikey.server_permissions }} - {{ translate('apiKeys', 'crafty', data['lang']) }} {{ apikey.crafty_permissions }}</td> - <td> - <button - class="btn btn-danger delete-api-key" + {% for apikey in data['api_keys'] %} + <tr> + <!--<td>{-{ apikey.token_id }-}</td>--> + <td>{{ apikey.name }}</td> + <td>{{ apikey.created.strftime('%d/%m/%Y %H:%M:%S') }}</td> + <td> + {% if apikey.superuser %} + <span class="text-success"> + <i class="fas fa-check-square"></i> {{ + translate('apiKeys', 'yes', data['lang']) }} + </span> + {% else %} + <span class="text-danger"> + <i class="far fa-times-square"></i> {{ + translate('apiKeys', 'no', data['lang']) }} + </span> + {% end %} + </td> + <td>{{ translate('apiKeys', 'server', data['lang']) }} {{ + apikey.server_permissions }} + {{ translate('apiKeys', 'crafty', data['lang']) }} {{ + apikey.crafty_permissions }}</td> + <td> + <button class="btn btn-danger delete-api-key" data-key-id="{{ apikey.token_id }}" - data-key-name="{{ apikey.name }}" - >{{ translate('panelConfig', 'delete', data['lang']) }}</button> - <button - class="btn btn-outline-primary get-a-token" + data-key-name="{{ apikey.name }}">{{ + translate('panelConfig', 'delete', data['lang']) + }}</button> + <button class="btn btn-outline-primary get-a-token" data-key-id="{{ apikey.token_id }}" - data-key-name="{{ apikey.name }}" - >{{ translate('apiKeys', 'getToken', data['lang']) }} - </button> - </td> - </tr> - {% end %} + data-key-name="{{ apikey.name }}">{{ + translate('apiKeys', 'getToken', data['lang']) }} + </button> + </td> + </tr> + {% end %} </tbody> </table> </div> @@ -109,68 +111,69 @@ <div class="col-md-5 col-sm-12"> <div class="card"> <div class="card-header header-sm d-flex justify-content-between align-items-center"> - <h4 class="card-title"><i class="fas fa-plus"></i> {{ translate('apiKeys', 'createNew', data['lang']) }}</h4> + <h4 class="card-title"><i class="fas fa-plus"></i> {{ translate('apiKeys', + 'createNew', data['lang']) }}</h4> </div> <div class="card-body"> <form id="user_form" class="forms-sample" method="post" - action="/panel/edit_user_apikeys"> + action="/panel/edit_user_apikeys"> {% raw xsrf_form_html() %} <input type="hidden" name="id" value="{{ data['user']['user_id'] }}"> <div class="form-group"> - <label class="form-label" for="username">{{ translate('apiKeys', 'name', data['lang']) }}<small - class="text-muted ml-1"> - {{ translate('apiKeys', 'nameDesc', data['lang']) }}</small> </label> + <label class="form-label" for="username">{{ translate('apiKeys', 'name', + data['lang']) }}<small class="text-muted ml-1"> - {{ + translate('apiKeys', 'nameDesc', data['lang']) }}</small> </label> <input type="text" class="form-control" name="name" id="name" - placeholder="API Key"> + placeholder="API Key"> </div> <table class="table table-hover mb-3"> <thead> - <tr class="rounded"> - <th>{{ translate('apiKeys', 'permName', data['lang']) }}</th> - <th>{{ translate('apiKeys', 'auth', data['lang']) }}</th> - </tr> + <tr class="rounded"> + <th>{{ translate('apiKeys', 'permName', data['lang']) }}</th> + <th>{{ translate('apiKeys', 'auth', data['lang']) }}</th> + </tr> </thead> <tbody> - {% for permission in data['server_permissions_all'] %} - <tr> - <td><label - for="permission_{{ permission.name }}">{{ permission.name }}</label> - </td> - <td> - <input type="checkbox" class="" - id="permission_{{ permission.name }}" - name="permission_{{ permission.name }}" value="1"> - </td> - </tr> - {% end %} - {% for permission in data['crafty_permissions_all'] %} - <tr> - <td><label - for="permission_{{ permission.name }}">{{ permission.name }}</label> - </td> - <td> - <input type="checkbox" class="" - id="permission_{{ permission.name }}" - name="permission_{{ permission.name }}" value="1"> - </td> - </tr> - {% end %} + {% for permission in data['server_permissions_all'] %} + <tr> + <td><label for="permission_{{ permission.name }}">{{ permission.name + }}</label> + </td> + <td> + <input type="checkbox" class="" + id="permission_{{ permission.name }}" + name="permission_{{ permission.name }}" value="1"> + </td> + </tr> + {% end %} + {% for permission in data['crafty_permissions_all'] %} + <tr> + <td><label for="permission_{{ permission.name }}">{{ permission.name + }}</label> + </td> + <td> + <input type="checkbox" class="" + id="permission_{{ permission.name }}" + name="permission_{{ permission.name }}" value="1"> + </td> + </tr> + {% end %} </tbody> </table> <label for="superuser">Superuser</label> - <input type="checkbox" class="" id="superuser" - name="superuser" value="1"> + <input type="checkbox" class="" id="superuser" name="superuser" value="1"> - <br/> + <br /> <button type="submit" class="btn btn-success mr-2"><i class="fas fa-plus"></i> Create </button> - <button type="reset" class="btn btn-light"><i - class="fas fa-undo-alt"></i> {{ translate('panelConfig', 'cancel', data['lang']) }} + <button type="reset" class="btn btn-light"><i class="fas fa-undo-alt"></i> {{ + translate('panelConfig', 'cancel', data['lang']) }} </button> </form> </div> @@ -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 @@ </script> -{% 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..0898d881 100644 --- a/app/frontend/templates/panel/server_schedules.html +++ b/app/frontend/templates/panel/server_schedules.html @@ -14,7 +14,8 @@ <div class="col-12"> <div class="page-header"> <h4 class="page-title"> - {{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }} + {{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ + data['server_stats']['server_id']['server_name'] }} <br /> <small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small> </h4> @@ -46,23 +47,23 @@ <h4 class="card-title"><i class="fas fa-calendar"></i> Scheduled Tasks</h4> {% if data['user_data']['hints'] %} <span class="too_small" title="{{ translate('serverSchedules', 'cannotSee', data['lang']) }}" , - data-content="{{ translate('serverSchedules', 'cannotSeeOnMobile', data['lang']) }}" , - data-placement="bottom"></span> + data-content="{{ translate('serverSchedules', 'cannotSeeOnMobile', data['lang']) }}" , + data-placement="bottom"></span> {% end %} <div><button - onclick="location.href=`/panel/add_schedule?id={{ data['server_stats']['server_id']['server_id'] }}`" - class="btn btn-info">Create New Schedule <i class="fas fa-pencil-alt"></i></button></div> + onclick="location.href=`/panel/add_schedule?id={{ data['server_stats']['server_id']['server_id'] }}`" + class="btn btn-info">Create New Schedule <i class="fas fa-pencil-alt"></i></button></div> </div> <div class="card-body"> <table class="table table-hover d-none d-lg-block responsive-table" id="schedule_table" width="100%" - style="table-layout:fixed;"> + style="table-layout:fixed;"> <thead> <tr class="rounded"> <th style="width: 2%; min-width: 10px;">ID</th> <th style="width: 23%; min-width: 50px;">Action</th> <th style="width: 40%; min-width: 50px;">Command</th> <th style="width: 10%; min-width: 50px;">Interval</th> - <th style="width: 10%; min-width: 50px;">Start Time</th> + <th style="width: 10%; min-width: 50px;">Next Run</th> <th style="width: 10%; min-width: 50px;">Enabled</th> <th style="width: 10%; min-width: 50px;">Edit</th> </tr> @@ -91,13 +92,17 @@ {% end %} </td> <td id="{{schedule.start_time}}" class="action"> - <p>{{schedule.start_time}}</p> + <p>{{schedule.next_run}}</p> </td> <td id="{{schedule.enabled}}" class="action"> - <input style="width: 10px !important;" type="checkbox" class="schedule-enabled-toggle" data-schedule-id="{{schedule.schedule_id}}" data-schedule-enabled="{{ 'true' if schedule.enabled else 'false' }}"> + <input style="width: 10px !important;" type="checkbox" class="schedule-enabled-toggle" + data-schedule-id="{{schedule.schedule_id}}" + data-schedule-enabled="{{ 'true' if schedule.enabled else 'false' }}"> </td> <td id="{{schedule.action}}" class="action"> - <button onclick="window.location.href='/panel/edit_schedule?id={{ data['server_stats']['server_id']['server_id'] }}&sch_id={{schedule.schedule_id}}'" class="btn btn-info"> + <button + onclick="window.location.href='/panel/edit_schedule?id={{ data['server_stats']['server_id']['server_id'] }}&sch_id={{schedule.schedule_id}}'" + class="btn btn-info"> <i class="fas fa-pencil-alt"></i> </button> <br> @@ -111,7 +116,8 @@ </tbody> </table> <hr /> - <table class="table table-hover d-block d-lg-none" id="mini_schedule_table" width="100%" style="table-layout:fixed;"> + <table class="table table-hover d-block d-lg-none" id="mini_schedule_table" width="100%" + style="table-layout:fixed;"> <thead> <tr class="rounded"> <th style="width: 25%; min-width: 50px;">Action</th> @@ -141,7 +147,8 @@ </td> </tr> <!-- Modal --> - <div class="modal fade" id="task_details_{{schedule.schedule_id}}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true"> + <div class="modal fade" id="task_details_{{schedule.schedule_id}}" tabindex="-1" role="dialog" + aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> @@ -177,17 +184,22 @@ {% end %} </li> <li id="{{schedule.start_time}}" class="action" style="border-top: .1em solid gray;"> - <h4>Start Time</h4> - <p>{{schedule.start_time}}</p> + <h4>Next Run</h4> + <p>{{schedule.next_run}}</p> </li> - <li id="{{schedule.enabled}}" class="action" style="border-top: .1em solid gray; border-bottom: .1em solid gray"> + <li id="{{schedule.enabled}}" class="action" + style="border-top: .1em solid gray; border-bottom: .1em solid gray"> <h4>Enabled</h4> - <input type="checkbox" class="schedule-enabled-toggle" data-schedule-id="{{schedule.schedule_id}}" data-schedule-enabled="{{ 'true' if schedule.enabled else 'false' }}"> + <input type="checkbox" class="schedule-enabled-toggle" + data-schedule-id="{{schedule.schedule_id}}" + data-schedule-enabled="{{ 'true' if schedule.enabled else 'false' }}"> </li> </ul> </div> <div class="modal-footer"> - <button onclick="window.location.href='/panel/edit_schedule?id={{ data['server_stats']['server_id']['server_id'] }}&sch_id={{schedule.schedule_id}}'" class="btn btn-info"> + <button + onclick="window.location.href='/panel/edit_schedule?id={{ data['server_stats']['server_id']['server_id'] }}&sch_id={{schedule.schedule_id}}'" + class="btn btn-info"> <i class="fas fa-pencil-alt"></i> Edit </button> <button data-sch={{ schedule.schedule_id }} class="btn btn-danger del_button"> @@ -215,12 +227,15 @@ color: white !important; ; } + .toggle-handle { background-color: white !important; } + .toggle-on { color: black !important; } + .toggle { height: 0px !important; } @@ -250,7 +265,7 @@ {% block js %} <script> - function debounce(func, timeout = 300){ + function debounce(func, timeout = 300) { let timer; return (...args) => { clearTimeout(timer); @@ -265,17 +280,17 @@ onstyle: 'success', offstyle: 'danger', }) - $('.schedule-enabled-toggle').each(function() { + $('.schedule-enabled-toggle').each(function () { const enabled = JSON.parse(this.getAttribute('data-schedule-enabled')); $(this).bootstrapToggle(enabled ? 'on' : 'off') }) - $('.schedule-enabled-toggle').change(function() { + $('.schedule-enabled-toggle').change(function () { const id = this.getAttribute('data-schedule-id'); const enabled = this.checked; fetch(`/api/v2/servers/{{data['server_id']}}/tasks/${id}`, { method: 'PATCH', - body: JSON.stringify({enabled}), + body: JSON.stringify({ enabled }), headers: { 'Content-Type': 'application/json', }, @@ -420,4 +435,4 @@ </script> -{% end %} +{% end %} \ No newline at end of file diff --git a/app/frontend/templates/server/wizard.html b/app/frontend/templates/server/wizard.html index 293eff32..53b045bf 100644 --- a/app/frontend/templates/server/wizard.html +++ b/app/frontend/templates/server/wizard.html @@ -32,13 +32,32 @@ <div class="row"> <div class="col-sm-12"> <div class="form-group"> - <label for="server_type">{{ translate('serverWizard', 'serverType', data['lang']) }}</label> - <select required class="form-control form-control-lg select-css" id="server_type" name="server_type" - onchange="serverTypeChange(this)"> - <option value="">{{ translate('serverWizard', 'selectType', data['lang']) }}</option> - {% for s in data['server_types'] %} - <option value="{{ s }}">{{ s.capitalize() }}</option> + <label for="server_jar">{{ translate('serverWizard', 'serverType', data['lang']) + }}</label> + {% if data['super_user'] %} + <select style="width: 90%;" required class="form-control form-control-lg select-css" id="server_jar" + name="server_jar" onchange="serverJarChange(this)"> + {% else %} + <select required class="form-control form-control-lg select-css" id="server_jar" name="server_jar" + onchange="serverJarChange(this)"> + {% end %} + <option value="None">{{ translate('serverWizard', 'selectType', data['lang']) }}</option> + {% for s in data['server_types'] %} + <option value="{{ s }}">{{ s.capitalize() }}</option> + {% end %} + </select> + {% if data['super_user'] %} + <i onclick="refreshCache()" id="refresh-cache" class="refresh-class fas fa-sync"></i> {% end %} + </div> + </div> + + <div class="col-sm-12"> + <div class="form-group"> + <label for="server_type">{{ translate('serverWizard', 'serverSelect', data['lang']) }}</label> + <select required class="form-control form-control-lg select-css" id="server_type" name="server_type" + onchange="serverTypeChange(this)"> + <option value="">{{ translate('serverWizard', 'selectServer', data['lang']) }}</option> </select> </div> </div> @@ -56,14 +75,14 @@ <div class="form-group"> <label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label> <input type="text" class="form-control" id="server_name" name="server_name" - placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required> + placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required> </div> </div> </div> <br /> <h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small - style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription', + style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription', data['lang']) }}</small></h4> <hr> <div class="row"> @@ -73,7 +92,7 @@ <label for="min_memory1">{{ translate('serverWizard', 'minMem', data['lang']) }} <small> - {{ translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label> <input type="number" class="form-control" id="min_memory1" name="min_memory" value="1" step="0.5" - min="0.5" required> + min="0.5" required> </div> </div> @@ -82,7 +101,7 @@ <label for="max_memory1">{{ translate('serverWizard', 'maxMem', data['lang']) }} <small> - {{ translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label> <input type="number" class="form-control" id="max_memory1" name="max_memory" value="2" step="0.5" - min="0.5" required> + min="0.5" required> </div> </div> @@ -91,7 +110,7 @@ <label for="port1">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small> - {{ translate('serverWizard', 'defaultPort', data['lang']) }}</small></label> <input type="number" class="form-control" id="port1" name="port" value="25565" step="1" min="1" - required> + required> </div> </div> <div class="col-sm-12"> @@ -100,7 +119,7 @@ <div class="card"> <div class="card-header p-2" id="Role-1"> <p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-1" aria-expanded="true" - aria-controls="collapseRole-1"> + aria-controls="collapseRole-1"> <i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang']) }} <small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate', data['lang']) }}</small> @@ -111,7 +130,7 @@ <div class="form-group"> {% for r in data['roles'] %} <span class="d-block menu-option"><label><input name="{{ r['role_id'] }}" - type="checkbox"> + type="checkbox"> {{ r['role_name'].capitalize() }}</label></span> {% end %} </div> @@ -152,7 +171,7 @@ <div class="form-group"> <label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label> <input type="text" class="form-control" id="server_name" name="server_name" value="" - placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required> + placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required> </div> </div> @@ -161,7 +180,7 @@ <label for="server">{{ translate('serverWizard', 'serverPath', data['lang']) }} <small>{{ translate('serverWizard', 'absoluteServerPath', data['lang']) }}</small></label> <input type="text" class="form-control" id="server_path" name="server_path" - placeholder="/var/opt/server" required> + placeholder="/var/opt/server" required> </div> </div> @@ -169,7 +188,7 @@ <div class="form-group"> <label for="server_jar">{{ translate('serverWizard', 'serverJar', data['lang']) }}</label> <input type="text" class="form-control" id="server_jar" name="server_jar" value="" - placeholder="paper.jar" required> + placeholder="paper.jar" required> </div> </div> @@ -178,7 +197,7 @@ </div> <br /> <h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small - style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription', + style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription', data['lang']) }}</small></h4> <hr> <div class="row"> @@ -188,7 +207,7 @@ <label for="min_memory2">{{ translate('serverWizard', 'minMem', data['lang']) }} <small> - {{ translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label> <input type="number" class="form-control" id="min_memory2" name="min_memory" value="1" step="0.5" - min="0.5" required> + min="0.5" required> </div> </div> @@ -197,7 +216,7 @@ <label for="max_memory2">{{ translate('serverWizard', 'maxMem', data['lang']) }} <small> - {{ translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label> <input type="number" class="form-control" id="max_memory2" name="max_memory" value="2" step="0.5" - min="0.5" required> + min="0.5" required> </div> </div> @@ -206,7 +225,7 @@ <label for="port2">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small> - {{ translate('serverWizard', 'defaultPort', data['lang']) }}</small></label> <input type="number" class="form-control" id="port2" name="port" value="25565" step="1" min="1" - required> + required> </div> </div> <div class="col-sm-12"> @@ -215,7 +234,7 @@ <div class="card"> <div class="card-header p-2" id="Role-2"> <p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-2" aria-expanded="true" - aria-controls="collapseRole-2"> + aria-controls="collapseRole-2"> <i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang']) }} <small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate', data['lang']) }}</small> @@ -226,7 +245,7 @@ <div class="form-group"> {% for r in data['roles'] %} <span class="d-block menu-option"><label><input name="{{ r['role_id'] }}" - type="checkbox"> + type="checkbox"> {{ r['role_name'].capitalize() }}</label></span> {% end %} </div> @@ -266,7 +285,7 @@ <div class="form-group"> <label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label> <input type="text" class="form-control" id="server_name" name="server_name" value="" - placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required> + placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required> </div> </div> @@ -275,7 +294,7 @@ <label for="server">{{ translate('serverWizard', 'zipPath', data['lang']) }} <small>{{ translate('serverWizard', 'absoluteZipPath', data['lang']) }}</small></label> <input type="text" class="form-control" id="server_path" name="server_path" - placeholder="/var/opt/server.zip" required> + placeholder="/var/opt/server.zip" required> </div> </div> @@ -294,7 +313,7 @@ <div class="form-group"> <label for="server_jar">{{ translate('serverWizard', 'serverJar', data['lang']) }}</label> <input type="text" class="form-control" id="server_jar" name="server_jar" value="" - placeholder="paper.jar" required> + placeholder="paper.jar" required> </div> </div> </div> @@ -303,7 +322,7 @@ <div class="col-sm-3"> <h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small - style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription', + style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription', data['lang']) }}</small></h4> <hr> <div class="row"> @@ -313,7 +332,7 @@ <label for="min_memory3">{{ translate('serverWizard', 'minMem', data['lang']) }} <small> - {{ translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label> <input type="number" class="form-control" id="min_memory3" name="min_memory" value="1" step="0.5" - min="0.5" required> + min="0.5" required> </div> </div> @@ -322,7 +341,7 @@ <label for="max_memory3">{{ translate('serverWizard', 'maxMem', data['lang']) }} <small> - {{ translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label> <input type="number" class="form-control" id="max_memory3" name="max_memory" value="2" step="0.5" - min="0.5" required> + min="0.5" required> </div> </div> @@ -331,7 +350,7 @@ <label for="port3">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small> - {{ translate('serverWizard', 'defaultPort', data['lang']) }}</small></label> <input type="number" class="form-control" id="port3" name="port" value="25565" step="1" min="1" - required> + required> </div> </div> @@ -341,7 +360,7 @@ <div class="card"> <div class="card-header p-2" id="Role-3"> <p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-3" - aria-expanded="true" aria-controls="collapseRole-3"> + aria-expanded="true" aria-controls="collapseRole-3"> <i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang']) }} <small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate', data['lang']) }}</small> @@ -352,7 +371,7 @@ <div class="form-group"> {% for r in data['roles'] %} <span class="d-block menu-option"><label><input name="{{ r['role_id'] }}" - type="checkbox"> + type="checkbox"> {{ r['role_name'].capitalize() }}</label></span> {% end %} </div> @@ -368,7 +387,7 @@ </div> </div> <div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select" - aria-hidden="true"> + aria-hidden="true"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> @@ -380,7 +399,7 @@ </div> <div class="modal-body"> <div class="tree-ctx-item" id="main-tree-div" data-path="" - style="overflow: scroll; max-height:75%;"> + style="overflow: scroll; max-height:75%;"> <input type="radio" id="main-tree-input" name="root_path" value="" checked> <span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path=""> <i class="far fa-folder"></i> @@ -401,7 +420,7 @@ </div> </div> <button id="zip_submit" type="submit" title="You must select server root dir first" disabled - class="btn btn-primary mr-2">{{ translate('serverWizard', 'importServerButton', data['lang']) + class="btn btn-primary mr-2">{{ translate('serverWizard', 'importServerButton', data['lang']) }}</button> <button type="reset" class="btn btn-danger mr-2">{{ translate('serverWizard', 'resetForm', data['lang']) }}</button> @@ -414,6 +433,10 @@ </div> </div> <style> + .refresh-class:hover { + cursor: grab; + } + .scroll { max-height: 12em; overflow-y: auto; @@ -673,6 +696,25 @@ }); } + function refreshCache() { + var token = getCookie("_xsrf") + document.getElementById("refresh-cache").classList.add("fa-spin") + $.ajax({ + type: "POST", + headers: { 'X-XSRFToken': token }, + url: '/ajax/jar_cache', + success: function () { + document.getElementById("refresh-cache").classList.remove("fa-sync"); + document.getElementById("refresh-cache").classList.remove("fa-spin"); + document.getElementById("refresh-cache").classList.add("fa-check"); + + setTimeout(function () { + location.reload(); + }, 2000); + }, + }); + } + </script> <script type="text/javascript"> var text = '{% raw data["js_server_types"] %}'; @@ -682,11 +724,21 @@ */ function serverTypeChange(selectObj) { // get the index of the selected option - var idx = selectObj.selectedIndex; + var idx = document.getElementById('server_type').selectedIndex; // get the value of the selected option - var which = selectObj.options[idx].value; + var cSelect = document.getElementById("server"); + try { + var which = document.getElementById('server_type').options[idx].value; + } catch { + while (cSelect.options.length > 0) { + cSelect.remove(0); + } + return; + } + let server_type = which.split('|')[0]; + let server = which.split('|')[1]; // use the selected option value to retrieve the list of items from the serverTypesLists array - cList = serverTypesLists[which]; + let cList = serverTypesLists[server_type]; // get the country select element via its known id var cSelect = document.getElementById("server"); // remove the current options from the country select @@ -696,7 +748,7 @@ } var newOption; // create new options ordered by ascending - cList.forEach(type => { + cList[server].forEach(type => { newOption = document.createElement("option"); newOption.value = which + "|" + type; // assumes option string and value are the same newOption.text = type; @@ -709,5 +761,47 @@ } }) } + + function serverJarChange(selectObj) { + let type_select = document.getElementById('server_jar') + let tidx = type_select.selectedIndex; + let val = type_select.options[tidx].value; + if (val == 'None') { + var jcSelect = document.getElementById("server_type"); + while (jcSelect.options.length > 0) { + jcSelect.remove(0); + } + serverTypeChange(selectObj); + return; + } + // get the index of the selected option + var jidx = selectObj.selectedIndex; + // get the value of the selected option + var jwhich = selectObj.options[jidx].value; + // use the selected option value to retrieve the list of items from the serverTypesLists array + jcList = Object.keys(serverTypesLists[jwhich]); + // get the country select element via its known id + var jcSelect = document.getElementById("server_type"); + // remove the current options from the country select + var jlen = jcSelect.options.length; + while (jcSelect.options.length > 0) { + jcSelect.remove(0); + } + var jnewOption; + // create new options ordered by ascending + jcList.forEach(type => { + jnewOption = document.createElement("option"); + jnewOption.value = jwhich + "|" + type; // assumes option string and value are the same + jnewOption.text = type; + // add the new option + try { + jcSelect.add(jnewOption); // this will fail in DOM browsers but is needed for IE + } + catch (e) { + jcSelect.appendChild(jnewOption); + } + }) + serverTypeChange(selectObj); + } </script> {% end %} \ No newline at end of file diff --git a/app/migrations/20220804_schedule_next_run.py b/app/migrations/20220804_schedule_next_run.py new file mode 100644 index 00000000..4fa423c7 --- /dev/null +++ b/app/migrations/20220804_schedule_next_run.py @@ -0,0 +1,16 @@ +# Generated by database migrator +import peewee + + +def migrate(migrator, database, **kwargs): + migrator.add_columns("schedules", next_run=peewee.CharField(default="")) + """ + Write your migrations here. + """ + + +def rollback(migrator, database, **kwargs): + migrator.drop_columns("schedules", ["next_run"]) + """ + Write your rollback migrations here. + """ diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index a930bfd0..041e4615 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -480,7 +480,8 @@ "save": "Save", "selectRole": "Select Role(s)", "selectRoot": "Select Archive Root Dir", - "selectType": "Select a Type", + "selectType": "Server Type (Vanilla, Servers, Modded, etc.)", + "selectServer": "Select a Server", "selectVersion": "Select a Version", "selectZipDir": "Select the directory in the archive you want us to unzip files from", "serverJar": "Server Executable File", @@ -488,6 +489,7 @@ "serverPath": "Server Path", "serverPort": "Server Port", "serverType": "Server Type", + "serverSelect": "Server Select", "serverVersion": "Server Version", "sizeInGB": "Size in GB", "zipPath": "Server Path" @@ -538,4 +540,4 @@ "userSettings": "User Settings", "uses": "Number of uses allowed (-1==No Limit)" } -} +} \ No newline at end of file diff --git a/app/translations/es_ES_incomplete.json b/app/translations/es_ES.json similarity index 64% rename from app/translations/es_ES_incomplete.json rename to app/translations/es_ES.json index 182b2c07..28f8d6e1 100644 --- a/app/translations/es_ES_incomplete.json +++ b/app/translations/es_ES.json @@ -11,12 +11,48 @@ "noAccess": "No tienes acceso a este recurso" }, "apiKeys": { + "apiKeys": "Claves API", + "auth": "Autorizado? ", + "buttons": "Botones", + "config": "Configuración", + "crafty": "Crafty: ", + "created": "Creado", + "createNew": "Crear un nuevo API Token", "deleteKeyConfirmation": "¿Quieres eliminar esta clave de API? Esto no se puede deshacer.", - "deleteKeyConfirmationTitle": "¿Eliminar la clave API ${keyId}?" + "deleteKeyConfirmationTitle": "¿Eliminar la clave API ${keyId}?", + "getToken": "Conseguir un Token", + "name": "Nombre", + "nameDesc": "Como te gustaria llamar a este API token? ", + "no": "No", + "pageTitle": "Editar las Claves API", + "permName": "Nombre del Permiso", + "perms": "Permisos", + "server": "Servidor: ", + "superUser": "Super User", + "yes": "Si" }, "base": { "doesNotWorkWithoutJavascript": "<strong>Aviso: </strong>¡Crafty no funciona correctamente cuando JavaScript no está habilitado!" }, + "credits": { + "developmentTeam": "Equipo de desarrollo", + "hugeDesc": "Un enorme", + "pageDescription": "Sin esta gente, Crafty no existiría", + "pageTitle": "Créditos", + "patreonDesc": "Patreon / Ko-fi supporters!", + "patreonOther": "Otro", + "patreonSupporter": "A nuestros Patreon / Ko-fi Supporters", + "patreonUpdate": "Última Actualización:", + "retiredStaff": "Staff retirado", + "subscriberName": "Nombre", + "subscriptionLevel": "Nivel", + "supportTeam": "Equipo de soporte y documentación", + "thankYou": "GRACIAS", + "translationDesc": "a nuestra comunidad, que tradujo!", + "translationName": "Nombre", + "translationTitle": "Traducciones de Idiomas", + "translator": "Traductores" + }, "dashboard": { "actions": "Acciones", "allServers": "Todos los servidores", @@ -30,10 +66,12 @@ "cannotSeeOnMobile": "¿No puedes verlo todo en móvil?", "cannotSeeOnMobile2": "Intenta desplazar la tabla desde los lados..", "clone": "Clonar", + "cloneConfirm": "¿Está seguro de que desea clonar este servidor? Este proceso puede llevar un tiempo", "cpuCores": "Nucleos de CPU", "cpuCurFreq": "Reloj de CPU Actual", "cpuMaxFreq": "Reloj de CPU Maximo", "cpuUsage": "Uso de CPU", + "crashed": "Crashed", "dashboard": "Panel de control", "delay-explained": "El agente/servicio ha iniciado recientemente y está retrasando el inicio de la instancia del servidor de Minecraft.", "host": "Host", @@ -53,13 +91,13 @@ "sendingCommand": "Enviando tu comando", "server": "Server", "servers": "Servers", + "size": "Tamaño del directorio del servidor", "start": "Iniciar", "starting": "Inicio-retrasado", "status": "Estado", "stop": "Detener", "version": "Versión", - "welcome": "Bienvenido a Crafty Controller", - "world": "Mundo" + "welcome": "Bienvenido a Crafty Controller" }, "datatables": { "i18n": { @@ -133,9 +171,13 @@ "eulaAgree": "Estás de acuerdo?", "eulaMsg": "Debes aceptar el EULA. Una copia del EULA de Mojang esta vinculada debajo de este mensaje.", "eulaTitle": "Aceptar EULA", + "fileTooLarge": "Subida fallida. Carga de archivo demasiado grande. Póngase en contacto con el administrador del sistema para obtener ayuda.", "hereIsTheError": "Aquí está el error.", "internet": "Hemos detectado que la maquina ejecutando Crafty no tiene acceso a internet. Las conexiones de clientes al servidor podrían estar limitadas.", + "no-file": "No se puede localizar el archivo solicitado. Verifique dos veces la ruta. Capaz Crafty no tiene los permisos adecuados?", "noJava": "Server {} fallo al iniciar con código de error: Detectamos que Java no esta instalado, Por favor instale java y inicie el servidor.", + "not-downloaded": "Parece que no podemos encontrar su archivo ejecutable. Capaz no ha terminado de descargarse o los permisos están configurados como ejecutables.", + "portReminder": "Detectamos que es la primera vez que se inicia {}. Asegúrese de configurar el puerto {} A través de su router/firewall para hacer el servidor accesible.", "start-error": "Servidor {} fallo al iniciar con código de error: {}", "terribleFailure": "¡Un terrible error!" }, @@ -150,16 +192,68 @@ "password": "Contraseña", "username": "Usuario" }, + "notify": { + "activityLog": "Registros de actividad", + "backupComplete": "Backup completado de manera exitosa en el servidor {}", + "backupStarted": "Backup iniciado para el servidor {}", + "downloadLogs": "Descargar registros de soporte?", + "finishedPreparing": "Terminamos la preparación de tus registros de soporte. Por favor presione el boton para descargar.", + "logout": "Cerrar Sesión", + "preparingLogs": " Por favor espere mientras preparamos los registros. Le enviaremos una notificación cuando estén listos. Esto puede tomar un rato en implementaciones grandes.", + "supportLogs": "Registros de soporte" + }, "panelConfig": { + "adminControls": "Controles de administrador", + "allowedServers": "Servidores Habilitados", + "assignedRoles": "Roles asignados", "cancel": "Cancelar", + "clearComms": "Limpiar comandos sin ejecutar.", "delete": "Eliminar", - "save": "Guardar" + "edit": "Editar", + "enabled": "Habilitado", + "newRole": "Agregar un nuevo rol", + "newUser": "Agregar nuevo Usuario", + "pageTitle": "Configuración del panel", + "role": "Rol", + "roles": "Roles", + "roleUsers": "Usuarios del rol", + "save": "Guardar", + "superConfirm": "Proceder solamente si queres que este usuario tenga acceso a TODO(todas las cuentas de usuarios, servidores, configuración del panel, etc...) Hasta puede quitarte tu permiso de superuser.", + "superConfirmTitle": "Habilitar superusers? Estas seguro?", + "user": "Usuario", + "users": "Usuarios" + }, + "rolesConfig": { + "config": "Configuracion de roles", + "configDesc": "Aca podes cambiar la configuracion de los roles", + "configUpdate": "Actualizado: ", + "created": "Creado: ", + "delRole": "Eliminar Rol", + "doesNotExist": "No podes elminiar algo que todavia no existe", + "pageTitle": "Editar Rol", + "pageTitleNew": "Nuevo Rol", + "permAccess": "Acceso?", + "permName": "Nombre de los permisos", + "permsServer": "Permisos que tiene este rol para esos servidores específicos.", + "roleConfigArea": "Zona de configuración de roles", + "roleDesc": "Como te gustaria llamar a este rol?", + "roleName": "Nombre del rol: ", + "rolePerms": "Permisos del rol", + "roleServers": "Servidores Habilitados", + "roleTitle": "Configuración de Roles", + "roleUserName": "Nombre de usuario", + "roleUsers": "Usuarios del rol: ", + "serverAccess": "Acceso?", + "serverName": "Nombre del servidor", + "serversDesc": "Servidores a los que este rol puede acceder" }, "serverBackups": { "backupAtMidnight": "¿Copia de seguridad automática a medianoche?", "backupNow": "¡Haga una copia de seguridad ahora!", "backupTask": "Se ha iniciado una tarea de copia de seguridad.", "cancel": "Cancelar", + "clickExclude": "Click para seleccionar las Exclusiones", + "compress": "Comprimir el Backup", "confirm": "Confirmar", "confirmDelete": "¿Quieres eliminar esta copia de seguridad? Esto no se puede deshacer.", "confirmRestore": "Esta seguro de que quiere restaurar desde este backup. Todos los archivos actuales del servidor seran cambiados al estado del backup y seran irrecuperables.", @@ -167,6 +261,9 @@ "delete": "Eliminar", "destroyBackup": "¿Destruir copia de seguridad \" + file_to_del + \"?", "download": "Descargar", + "excludedBackups": "Rutas Excluidas: ", + "excludedChoose": "Elija las rutas que desea excluir de sus backups", + "exclusionsTitle": "Exclusiones del backup.", "maxBackups": "Máxima cantidad de Copias de seguridad", "maxBackupsDesc": "Crafty no almacenará más de N copias de seguridad, eliminando la más antigua (Ingrese 0 para mantenerlas todas)", "options": "Opciones", @@ -174,6 +271,7 @@ "restore": "Restaurar", "restoring": "Restaurando copia de seguridad. Esto puede tomar un tiempo. Sea paciente.", "save": "Guardar", + "shutdown": "Apague el servidor durante la duración de la copia del backup.", "size": "Tamaño", "storageLocation": "Ubicación de almacenamiento", "storageLocationDesc": "¿Dónde quieres almacenar las copias de seguridad?" @@ -183,6 +281,8 @@ "bePatientDeleteFiles": "Tenga paciencia mientras eliminamos su servidor del panel de Crafty y eliminamos todos los archivos. Esta pantalla se cerrará en unos momentos.", "bePatientUpdate": "Tenga paciencia mientras actualizamos el servidor. El tiempo de descarga puede variar según la velocidad del Internet...<br /> Esta pantalla se actualizará en unos momentos.", "cancel": "Cancelar", + "crashTime": "Tiempo de espera por crash", + "crashTimeDesc": "Cuanto tiempo se debe esperar después de que crafty considere su servidor como crasheado?", "deleteFilesQuestion": "¿Eliminar archivos del servidor de la máquina?", "deleteFilesQuestionMessage": "¿Le gustaría que Crafty elimine todos los archivos del servidor de la máquina host? <br><br><strong>Esto incluye backups del servidor.</strong>", "deleteServer": "Eliminar Servidor", @@ -190,6 +290,9 @@ "deleteServerQuestionMessage": "¿Estás seguro de que desea eliminar este servidor? Después de esto no hay vuelta atrás...", "exeUpdateURL": "URL de actualización para el ejecutable del Servidor", "exeUpdateURLDesc": "URL de descarga directa para actualizaciones.", + "javaNoChange": "No sobrescribir", + "javaVersion": "Sobrescribir versión de Java.", + "javaVersionDesc": "Si vas a sobrescribir la versión de Java, Asegúrese de que ponga la localizacion de java con ' ' (comillas).", "noDelete": "No, vuelve atrás.", "noDeleteFiles": "No, solo remover del panel.", "removeOldLogsAfter": "Eliminar registros antiguos después de", @@ -266,11 +369,13 @@ "fileReadError": "Error de lectura del archivo", "files": "Archivos", "keybindings": "Atajos de teclado", + "loadingRecords": "Cargando Archivos...", "noDelete": "No", "noscript": "El administrador de archivos no funciona sin JavaScript ", "rename": "Renombrar", "renameItemQuestion": "Cuál debería ser el nuevo nombre?", "save": "Guardar", + "size": "Alternar tamaño del editor.", "stayHere": "NO SALGA DE ESTA PÁGINA", "unsupportedLanguage": "Advertencia: este no es un tipo de archivo admitido", "unzip": "Descomprimir (UnZip)", @@ -284,6 +389,41 @@ "loadingBannedPlayers": "Cargando jugadores baneados", "players": "Jugadores" }, + "serverScheduleConfig": { + "backup": "Backup del Servidor", + "basic": "Básico", + "children": "Tareas 'hijos' enlazados: ", + "command": "Comando", + "command-explain": "Que comandó quiere que sea ejecutado, no incluya el '/'", + "cron": "Cron", + "cron-explain": "Ponga su configuración de cron. -- NOTA: Pagina recomendada: 'https://crontab.guru'", + "custom": "Comando Customizado", + "days": "Días", + "enabled": "Habilitado", + "hours": "Horas", + "interval": "Intervalo", + "interval-explain": "Con qué frecuencia quieres que se ejecute esta tarea?", + "minutes": "Minutos", + "offset": "Retraso", + "offset-explain": "Cuanto tiempo se debe esperar para ejecutar esta tarea después de la primera? (En Segundos).", + "one-time": "Borrar después de su ejecución?", + "parent": "Seleccione una tarea programada 'padre'", + "parent-explain": "Qué tarea programada debería activar esta?", + "reaction": "Reaccion", + "restart": "Reiniciar el Servidor", + "start": "Iniciar el Servidor", + "stop": "Apagar el Servidor", + "time": "Horario", + "time-explain": "A que hora quiere que la tarea programada se ejecute?" + }, + "serverSchedules": { + "areYouSure": "Borrar tarea programada?", + "cancel": "Cancelar", + "cannotSee": "No puede pude ver todo?", + "cannotSeeOnMobile": "Intente hacer clic en una tarea programada para obtener todos los detalles.", + "confirm": "Confirmar", + "confirmDelete": "Estás seguro de que desea eliminar esta tarea programada? Esto no se puede deshacer." + }, "serverStats": { "cpuUsage": "Uso de CPU", "description": "Descripción", @@ -295,6 +435,7 @@ "serverStarted": "Servidor Iniciado", "serverStatus": "Estado del Servidor", "serverTime": "Hora UTC", + "serverTimeZone": "Zona horaria del servidor", "serverUptime": "Actividad del Servidor", "starting": "Inicio-retrasado", "unableToConnect": "No se pudo conectar", @@ -303,6 +444,7 @@ "serverTerm": { "commandInput": "Introducir tu comando", "delay-explained": "El agente/servicio ha recientemente iniciado y está retrasando el inicio de la instancia del servidor de Minecraft.", + "downloading": "Descargando...", "restart": "Reiniciar", "sendCommand": "Enviar comando", "start": "Iniciar", @@ -318,8 +460,11 @@ "autoCreate": "Si no se selecciona ninguno, ¡Crafty creara uno!", "bePatient": "Por favor tenga paciencia, ya que estamos ' + (importing ? 'import' : 'download') + ' el servidor.", "buildServer": "Construir Servidor!", + "clickRoot": "Click aquí para seleccionar el directorio raiz.", + "close": "Cerrar", "defaultPort": "25565 (Por defecto)", "downloading": "Descargando Servidor...", + "explainRoot": "Por favor, haga click en el botón de abajo para seleccionar el directorio raiz de su servidor.", "importing": "Importando Servidor...", "importServer": "Importar Servidor existente", "importServerButton": "Importar Servidor!", @@ -331,9 +476,12 @@ "quickSettings": "Ajustes rápidos", "quickSettingsDescription": "No te preocupes, puedes cambiarlos más tarde.", "resetForm": "Limpiar formulario", + "save": "Guardar", "selectRole": "Seleccionar Grupo(s)", + "selectRoot": "Seleccione el directorio raíz del archivo.", "selectType": "Selecciona un tipo", "selectVersion": "Selecciona una versión", + "selectZipDir": "Seleccione el directorio donde quiere que se extraigan los archivos.", "serverJar": "Archivo Jar del servidor", "serverName": "Nombre del servidor", "serverPath": "Dirección del servidor", @@ -351,5 +499,42 @@ "navigation": "Navegación", "newServer": "Crear nuevo Servidor", "servers": "Servers" + }, + "userConfig": { + "apiKey": "Claves API", + "auth": "Autorizado? ", + "config": "Configuración", + "configArea": "Area de Configuracion de Usuarios", + "configAreaDesc": "Aca modificas la configuración de todos tus usuarios.", + "confirmDelete": "Está seguro de que quiere borrar a este usuario? Esta acción es irreversible.", + "craftyPermDesc": "Permisos de Crafty que tiene este usuario.", + "craftyPerms": "Permisos de Crafty: ", + "created": "Creado: ", + "deleteUser": "Borrar Usuario: ", + "deleteUserB": "Borrar Usuario", + "delSuper": "No puedes borrar un super user", + "enabled": "Habilitado", + "gravDesc": "Este mail se va a usar solamente para conseguir el Gravatar. Crafty no va a usar esta información bajo ninguna circunstancia más que para conseguir el Gravatar™", + "gravEmail": "Gravatar™ Email", + "lastIP": "Ultima IP: ", + "lastLogin": "Última Sesión: ", + "lastUpdate": "Última Actualización: ", + "leaveBlank": "Para editar el usuario sin cambiar la contraseña, no completar nada.", + "member": "Miembro?", + "notExist": "No se puede borrar algo que no existe!", + "pageTitle": "Editar Usuario.", + "pageTitleNew": "Crear Usuario.", + "password": "Nueva Contraseña", + "permName": "Nombre del Permiso", + "repeat": "Repita la contraseña.", + "roleName": "Nombre del rol.", + "super": "Super User", + "userLang": "Idioma del usuario.", + "userName": "Nombre de usuario.", + "userNameDesc": "Como quieres llamar a este usuario?", + "userRoles": "Roles del usuario.", + "userRolesDesc": "Roles que tiene este usuario.", + "userSettings": "Configuracion de Usuario.", + "uses": "Número de usos habilitados. (-1 == sin límite)." } } diff --git a/app/translations/pl_PL.json b/app/translations/pl_PL.json new file mode 100644 index 00000000..90d42e7a --- /dev/null +++ b/app/translations/pl_PL.json @@ -0,0 +1,540 @@ +{ + "404": { + "contact": "Podrzebujesz pomocy? Zapraszamy na serwer discord Crafty Controler", + "notFound": "Strony nie znaleziono", + "unableToFind": "Nie znaleźliśmy strony której szukasz. Spróbój ponownie, albo wróć się o stronę i ją odśwież" + }, + "accessDenied": { + "accessDenied": "Nie posiadasz do tego dostępu", + "contact": "Podrzebujesz pomocy? Zapraszamy na serwer discord Crafty Controler", + "contactAdmin": "Skontaktuj się z administratorem aby uzyskać dostęp, jeśli myślisz że powinieneś mieć dostęp, skontaktuj się z supportem.", + "noAccess": "Nie masz dostępu do tych zasobów" + }, + "apiKeys": { + "apiKeys": "Klucze API", + "auth": "Authorized? ", + "buttons": "Przyciski", + "config": "Config", + "crafty": "Crafty: ", + "created": "Stworzono", + "createNew": "Stwórz nowy klucz API", + "deleteKeyConfirmation": "Czy chcesz usunąć ten klucz API? Nie można tego cofnąć.", + "deleteKeyConfirmationTitle": "Usunąć Klucz API ${keyId}?", + "getToken": "Zdobądź token", + "name": "Nazwa", + "nameDesc": "Jak chcesz nazwać ten klucz API? ", + "no": "Nie", + "pageTitle": "Edytuj klucze API użytkownika", + "permName": "Nazwa permisji", + "perms": "Permisje", + "server": "Serwer: ", + "superUser": "Super użytkownik", + "yes": "Tak" + }, + "base": { + "doesNotWorkWithoutJavascript": "<strong>Uwaga: </strong>Crafty nie działa gdy JavaScript jest wyłączone!" + }, + "credits": { + "developmentTeam": "Drużyna Deweloperów", + "hugeDesc": "Wielkie", + "pageDescription": "Bez tych ludzi nie miał być Craftiego!", + "pageTitle": "Podziękowania", + "patreonDesc": "dla naszych ludzi wsparcia z Patreon / Ko-fi!", + "patreonOther": "Inne", + "patreonSupporter": "Patreon / Ko-fi Wsparcie", + "patreonUpdate": "Ostatni Update:", + "retiredStaff": "Wycofany personel", + "subscriberName": "Nazwa", + "subscriptionLevel": "Poziom", + "supportTeam": "Pomoc i Drużyna Dokumentacji", + "thankYou": "DZIĘKUJEMY WAM", + "translationDesc": "dla naszej społeczności która tłumaczy!", + "translationName": "Nazwa", + "translationTitle": "Język tłumaczenia", + "translator": "Tłumacze" + }, + "dashboard": { + "actions": "Akcje", + "allServers": "Wszystkie serwery", + "avg": "Avg", + "backups": "Backupy", + "bePatientClone": "Poczekaj, gdy my klonujemy serwer.<br /> Strona za chwilę się odświeży", + "bePatientRestart": "Poczekaj, gdy my restartujemy twój serwer.<br /> Strona za chwilę się odświeży", + "bePatientStart": "Poczekaj, gdy my właczamy twój serwer.<br /> Strona za chwilę się odświeży", + "bePatientStop": "Poczekaj, gdy my zatrzymujemy twój serwer.<br /> Strona za chwilę się odświeży", + "cannotSee": "Nie widzisz wszystkiego?", + "cannotSeeOnMobile": "Nie widzisz wszystkiego na telefonie?", + "cannotSeeOnMobile2": "Spróbój obrócić telefon poziomo.", + "clone": "Klonuj", + "cloneConfirm": "Jesteś pewien że chcesz skonować ten serwer? To trochę zajmie.", + "cpuCores": "Rdzenie procesora", + "cpuCurFreq": "GHz Procesora", + "cpuMaxFreq": "Maksymalne GHz Procesora", + "cpuUsage": "Użycie procesora", + "crashed": "Zcrashował", + "dashboard": "Dashboard", + "delay-explained": "Crafty niedawno się odpalił, właczanie serwera będzie trochę opóźnione", + "host": "Host", + "kill": "Zabij proces", + "killing": "Zabijanie procesu...", + "lastBackup": "Ostatni:", + "max": "Maks.", + "memUsage": "Użycie RAMu", + "motd": "Wiadomość dnia", + "newServer": "Stwórz nowy serwer", + "nextBackup": "Next:", + "no-servers": "Nie posiadasz żadnych serwerów. Aby zacząć, kliknij", + "offline": "Wyłączony", + "online": "Włączony", + "players": "Gracze", + "restart": "Restart", + "sendingCommand": "Wysyłanie twojej komendy", + "server": "Serwer", + "servers": "Serwery", + "size": "Rozmiar serwera", + "start": "Start", + "starting": "Opóźniony-Start", + "status": "Status", + "stop": "Zatrzymaj", + "version": "Wersja", + "welcome": "Witamy w Crafty Controller" + }, + "datatables": { + "i18n": { + "aria": { + "sortAscending": ": aktywuj, aby sortować kolumny w góre", + "sortDescending": ": aktywuj, aby sortować kolumny w dół" + }, + "buttons": { + "collection": "Kolekcja <span class='ui-button-icon-primary ui-icon ui-icon-triangle-1-s'/>", + "colvis": "Widoczność kolumn", + "colvisRestore": "Przywróć widoczność", + "copy": "Kopiuj", + "copyKeys": "Wciśnij ctrl albo u2318 + C aby skopiować tabelę danych do schowka.<br><br>Aby anulować, kliknij tą wiadomość albo wciśnij escape.", + "copySuccess": { + "1": "Skopiowano 1 rząd do schowka", + "_": "Skopiowano %d rządów do schowka" + }, + "copyTitle": "Kopiuj do schowka", + "csv": "CSV", + "excel": "Eksel", + "pageLength": { + "1": "Pokaż 1 rząd", + "-1": "Pokaż wszystkie rzędy", + "_": "Pokaż %d rządów" + }, + "pdf": "PDF", + "print": "Wydrukuj" + }, + "decimal": "", + "emptyTable": "Brak danych w tej tabeli danych", + "info": "Pokazywanie _START_ to _END_ of _TOTAL_ wejść", + "infoEmpty": "Pokazywanie 0 do 0 z 0 wejść", + "infoFiltered": "(filtered from _MAX_ total entries)", + "infoPostFix": "", + "lengthMenu": "Pokazuj _MENU_ entries", + "loadingRecords": "Wczytywanie...", + "paginate": { + "first": "Pierwsze", + "last": "Ostatnie", + "next": "Następne", + "previous": "Poprzednie" + }, + "processing": "Przetwarzanie...", + "search": "Szukaj:", + "select": { + "cells": { + "0": "Kliknij na komórkę aby ją zaznaczyć", + "1": "%d komórka zaznaczonych", + "_": "%d komórek zaznaczonych" + }, + "columns": { + "0": "Kliknij na kolumnę aby ją zaznaczyć", + "1": "%d kolumna zaznaczona", + "_": "%d kolumn zaznaczonych" + }, + "rows": { + "0": "Click on a row to select it", + "1": "%d row selected", + "_": "%d rows selected" + } + }, + "thousands": ",", + "zeroRecords": "Nie znaleziono pasujacego wyniku" + } + }, + "error": { + "contact": "Podrzebujesz pomocy? Zapraszamy na serwer discord Crafty Controler", + "embarassing": "Oh, więc, to jest żenujące.", + "error": "Błąd!", + "eulaAgree": "Czy się zgadzasz?", + "eulaMsg": "Musisz się zgodzić na EULA. Kopia EULA Mojangu jest zalinkowana pod tą wiadomością.", + "eulaTitle": "Zgódź się na EULA", + "fileTooLarge": "Upload nie udany. Plik jest za duży. Skontaktuj się z administratorem dla pomocy.", + "hereIsTheError": "Tu jest problem", + "internet": "Zauważyliśmy że Crafty nie ma dostępu do internetu. Połączenie klientów z Craftym może być utrudnione.", + "no-file": "Nie możemy znaleść żądanego pliku. Sprawdź ścieżkę. Czy Crafty ma odpowiednie uprawnienia?", + "noJava": "Serwer {} nie mógł się odpalić z powodu: Zauważyliśmy że Java nie jest zainstalowana. Proszę zainstaluj Javę, a następnie włącz serwer.", + "not-downloaded": "Nie możemy znaleść twojego pliku serwera. Czy skończył się pobierać? Czy permisje są ustawione na wykonywanle?", + "portReminder": "Zauważyliśmy że to jest pierwszy raz {} kiedy był włączony. Upewnij się że otworzyłeś port {} na swoim routerze/firewallu aby korzystać z tego poza domem.", + "start-error": "Serwer {} nie mógł się odpalić z powodu: {}", + "terribleFailure": "Okropny błąd!" + }, + "footer": { + "allRightsReserved": "Wszelkie Prawa zastrzeżone", + "copyright": "Copyright", + "version": "Wersja" + }, + "login": { + "forgotPassword": "Zapomniałem hasła", + "login": "Zaloguj się", + "password": "Hasło", + "username": "Nazwa użytkownika" + }, + "notify": { + "activityLog": "Logi Aktywności", + "backupComplete": "Backup zakończony poprawnie dla serwera {}", + "backupStarted": "Backup zaczęty dla serwera {}", + "downloadLogs": "Pobrać Logi Supportu?", + "finishedPreparing": "Skończyliśmy przygotowywać logi supportu. Kliknij tutaj, aby je pobrać", + "logout": "Wyloguj się", + "preparingLogs": " Poczekaj kiedy my przygotowujemy twoje logi ... Wyślemy ci powiadomienie kiedy będą gotowe. To trochę zajmie na dużych serwerach.", + "supportLogs": "Logi Pomocnicze" + }, + "panelConfig": { + "adminControls": "Ustawienia Admina", + "allowedServers": "Zezwolone serwery", + "assignedRoles": "Przypisane role", + "cancel": "Anuluj", + "clearComms": "Wyczyść nie wykonane komendy", + "delete": "Usuń", + "edit": "Edytuj", + "enabled": "Wlączone", + "match": "Hasła muszą być takie same", + "newRole": "Dodaj nową role", + "newUser": "Dodaj nowego użytkownika", + "pageTitle": "Ustawienia Panelu", + "role": "Role", + "roles": "Roles", + "roleUsers": "Role użytkownika", + "save": "Zapisz", + "superConfirm": "Zapisz jeśli chcesz aby ten użytkownik miał dostęp do WSZYSTKIEGO (wszyscy użytkownicy, konta, serwery, ustawienia panelu, itp.). Mogą oni nawet usunąć twoje prawa SuperUżytkownika.", + "superConfirmTitle": "Włączyć SuperUżytkownika? Jesteś pewien?", + "user": "Użytkownik", + "users": "użytkownicy" + }, + "rolesConfig": { + "config": "Zarządzanie rolami", + "configDesc": "Tu możesz zmienić konfiguracje roli", + "configUpdate": "Ostatnio aktualizowane: ", + "created": "Stworzone: ", + "delRole": "Usuń role", + "doesNotExist": "Nie możesz usunąć czegoś co nie istnieje", + "pageTitle": "Edytuj role", + "pageTitleNew": "Nowa rola", + "permAccess": "Dostęp?", + "permName": "Nazwa permisji", + "permsServer": "Permisje tej roli dla tych wyspecializowanych serwerów", + "roleConfigArea": "Ustawienia ról", + "roleDesc": "Jak chciałbyć nazwać te rolę?", + "roleName": "Nazwa roli: ", + "rolePerms": "Permisje roli", + "roleServers": "Zezwolone serwery", + "roleTitle": "Ustawienia roli", + "roleUserName": "Nazwa użytkownika", + "roleUsers": "Dostęp do ról: ", + "serverAccess": "Dostęp?", + "serverName": "Nazwa serwera", + "serversDesc": "Serwery które mają tą role mają dostęp" + }, + "serverBackups": { + "backupAtMidnight": "Auto-backup o północy?", + "backupNow": "Backup Teraz!", + "backupTask": "Backup został rozpoczęty.", + "cancel": "Anuluj", + "clickExclude": "Kliknij aby zaznaczyć wyjątki", + "compress": "Skompresuj backup", + "confirm": "Akceptuj", + "confirmDelete": "Czy chcesz usunąć ten backup? Nie można tego cofnąć.", + "confirmRestore": "Czy jesteś pewien że chcesz przywrócić z tego backupu. Wszystkie pliki powrócą do stanu z backupu.", + "currentBackups": "Backupy Teraz", + "delete": "Usuń", + "destroyBackup": "Zniszcz Backup \" + file_to_del + \"?", + "download": "Pobierz", + "excludedBackups": "Wykluczone ścieżki: ", + "excludedChoose": "Wybierz ścieżki do wykluczenia z backupu", + "exclusionsTitle": "Wykluczenia backupu", + "maxBackups": "Maks. Backupów", + "maxBackupsDesc": "Crafty nie będzie zbierał więcej niż N backupów, zacznie usuwać od nadstarszych (wpisz 0, aby zatrzymać nieskończoną ilość)", + "options": "Opcje", + "path": "Ścieżka", + "restore": "Przywróć", + "restoring": "Przywracanie backupu. To trochę zajmie. Bądź cierpliwy.", + "save": "Zapisz", + "shutdown": "Wyłącz serwer na czas backupu", + "size": "Rozmiar", + "storageLocation": "Ścieżka zapisywania", + "storageLocationDesc": "Gdzie chcesz trzymać backupy?" + }, + "serverConfig": { + "bePatientDelete": "Poczekaj, aż usuniemy twój serwer. Strona za chwilę się zamknie.", + "bePatientDeleteFiles": "Poczekaj, aż usuniemy twój serwer i jego pliki. Strona za chwilę się zamknie.", + "bePatientUpdate": "Poczekaj kiedy my aktualizujemy twój serwer. Pobieranie zależy od prędkości twojego internetu.<br /> Strona się odświeży za chwile.", + "cancel": "Anuluj", + "crashTime": "Crash wyszedł poza limit czasu", + "crashTimeDesc": "How long should we wait before we consider your server as crashed?", + "deleteFilesQuestion": "Usuń pliki serwera z maszyny?", + "deleteFilesQuestionMessage": "Czy chcesz aby Crafty usunął wszystkie pliki tego serwera? <br><br><strong>To zawiera backupy.</strong>", + "deleteServer": "Usuń serwer", + "deleteServerQuestion": "Usunąć serwer?", + "deleteServerQuestionMessage": "Jesteś pewien że chcesz usunąć ten serwer? To jest nie odwracalne...", + "exeUpdateURL": "URL pliku egzekucyjnego serwera", + "exeUpdateURLDesc": "Bezpośredni link dla aktualizacji.", + "javaNoChange": "Nie nadpisuj", + "javaVersion": "Nadpisz wersję Javy", + "javaVersionDesc": "Jeśli chcesz nadpisać wersję javy. Upewnij się że twoja wersja javy w 'komendzie egzekucyjnej' jest zawinięta w cytaty (podstatowa zmienna 'java' jest wykluczona )", + "noDelete": "Nie, wróć się", + "noDeleteFiles": "Nie, tylko usuń z panelu", + "removeOldLogsAfter": "Usuń stare logi po", + "removeOldLogsAfterDesc": "Ile dni musi mieć log aby został usunięty (0 oznacza że nie będziemy usuwać)", + "save": "Zapisz", + "sendingDelete": "Usuwanie serwera", + "sendingRequest": "Wysyłanie twojej prośby...", + "serverAutoStart": "Auto-Włączanie serwera", + "serverAutostartDelay": "Opóźnienie auto-włączenia serwera", + "serverAutostartDelayDesc": "Opóźnienie auto-włączenia (Jeśli włączone poniżej)", + "serverCrashDetection": "Detekcja Crashu serwera", + "serverExecutable": "Plik egzekucyjny", + "serverExecutableDesc": "Plik egzekucyjnego serwera", + "serverExecutionCommand": "Komenda włączania serwera", + "serverExecutionCommandDesc": "Co będzie odpalone w ukrytej konsoli", + "serverIP": "IP serwera", + "serverIPDesc": "IP, na jakim Crafty powinien połączyć dla statystyk (Spróbój prawdziwe ip zamiast 127.0.0.1 jeśli masz problemy)", + "serverLogLocation": "Lokalizacja logów serwera", + "serverLogLocationDesc": "Ścieżka do logów serwera", + "serverName": "Nazwa serwera", + "serverNameDesc": "Jak chciałbyć nazwać ten serwer", + "serverPath": "Ścieżka serwera", + "serverPathDesc": "Pełna ścieżka serwera (nie licząc pliku startu serwera)", + "serverPort": "Port serwera", + "serverPortDesc": "Port na jakim Crafty ma szukać statystyk", + "serverStopCommand": "Komenda wyłączenia serwera", + "serverStopCommandDesc": "Komenda, którą wyśle crafty aby wyłączyć serwer", + "stopBeforeDeleting": "Zatrzymaj serwer przed jego usunięciem", + "update": "Aktualizuj plik serwera", + "yesDelete": "Tak, usuń", + "yesDeleteFiles": "Tak, usuń pliki" + }, + "serverConfigHelp": { + "desc": "Tutaj możesz zmienić konfigurację serwera", + "perms": [ + "<code>Nie</code> zalecamy zmieniania ścieżek serwera którymi zarządza Crafty.", + "Zmienianie ścieżek <code>MOŻE</code> zniszczyć rzeczy, szczególnie na Linuksie i podobnych systemach gdzie permisje plików są bardziej zamknięte.", + "<br /><br/>", + "Jeśli czujesz że musisz zmienić to, gdzie twój serwer się znajduje, możesz to zrobić, pod warunkiem że dasz mu permisję \"crafty\" do odczytu i zapisu w ścieżce serwera.", + "<br />", + "<br />", + "Na Linuksie najlepiej jest to zrobić wpisując taką komendę:<br />", + "<code>", + " sudo chown crafty:crafty /ścieżka/do/twojego/serwera -R<br />", + " sudo chmod 2775 /ścieżka/do/twojego/serwera -R<br />", + "</code>" + ], + "title": "Konfiguracja serwera" + }, + "serverDetails": { + "backup": "Backup", + "config": "Konfiguracja", + "files": "Pliki", + "logs": "Logi", + "playerControls": "Player Management", + "schedule": "Harmonogram", + "serverDetails": "Detale serwera", + "terminal": "Konsola" + }, + "serverFiles": { + "clickUpload": "Kliknij tutaj, aby wybrać twoje pliki", + "close": "Zamknij", + "createDir": "Nowa ścieżka", + "createDirQuestion": "Jak chcesz nazwać nową ścieżkę?", + "createFile": "Nowy plik", + "createFileQuestion": "Jak chcesz nazwać nowy plik?", + "default": "Domyślne", + "delete": "Usuń", + "deleteItemQuestion": "Czy jesteś pewien że chcesz usunąć \" + name + \"?", + "deleteItemQuestionMessage": "Usuwasz \\\"\" + path + \"\\\"!<br/><br/>Ta akcja jest nieodwracalna i zostanie usunięta na zawsze!", + "download": "Pobierz", + "editingFile": "Edytuję plik", + "error": "Error while getting files", + "fileReadError": "Error odczytu pliku", + "files": "Pliki", + "keybindings": "Skróty klawiszowe", + "loadingRecords": "Wczytywanie plików...", + "noDelete": "Nie", + "noscript": "Przeglądarka plików nie działa bez Javascript", + "rename": "Zmień nazwę", + "renameItemQuestion": "Jaka ma być nowa nazwa?", + "save": "Zapisz", + "size": "Włącz zmienianie rozmiaru edytora", + "stayHere": "NIE WYCHODŹ Z TEJ STRONY!", + "unsupportedLanguage": "Uwaga: To nie jest wspierany typ pliku", + "unzip": "Unzip", + "upload": "Upload", + "uploadTitle": "Wyślij pliki do: ", + "waitUpload": "Poczekaj, aż wyślemy twoje pliki... To może chwilkę zająć.", + "yesDelete": "Tak, zdaję sobie sprawę z konsekfencji" + }, + "serverPlayerManagement": { + "bannedPlayers": "Zbanowani gracze", + "loadingBannedPlayers": "Wczytuję zbanowanych graczy", + "players": "Gracze" + }, + "serverScheduleConfig": { + "backup": "Backup Serwera", + "basic": "Podstawowy", + "children": "Połączone zadania podrzędne: ", + "command": "Komenda", + "command-explain": "Jaką komendę chcesz abyśmy wykonali? Nie pisz '/' z przodu", + "cron": "Cron", + "cron-explain": "Tutaj wpisz cron string -- Uwaga: 0 = Poniedziałek jest ostatnią opcją.", + "custom": "Własna komenda", + "days": "dni", + "enabled": "Włączony", + "hours": "Godziny", + "interval": "Interwał", + "interval-explain": "Jak często mamy to wykonywać?", + "minutes": "Minuty", + "offset": "Opóźnienie przesunięcia", + "offset-explain": "Ile powinniśmy poczekać po wykonaniu pierwszego zadania? (W sekundach)", + "one-time": "Usuń po wykonaniu", + "parent": "Wybierz harmonogram rodzica", + "parent-explain": "Który harmonogram powinien wykonywać ten?", + "reaction": "Reakcja", + "restart": "Restart Serwera", + "start": "Start Serwera", + "stop": "Wyłącz Serwer", + "time": "Czas", + "time-explain": "O jakim czasie ma wykonać się ten harmonogram?" + }, + "serverSchedules": { + "areYouSure": "Usuń zaplanowane (zadanie)?", + "cancel": "Anuluj", + "cannotSee": "Nie widzisz wszystkiego?", + "cannotSeeOnMobile": "Spróbój kliknąć na zadanie dla detali.", + "confirm": "Zapisz", + "confirmDelete": "Czy chcesz usunąć ten zaplanowany task? Jest to nieodwracalne." + }, + "serverStats": { + "cpuUsage": "Użycie procesora", + "description": "Opis", + "errorCalculatingUptime": "Błąd obliczania czasu pracy", + "memUsage": "Użycie RAMu", + "offline": "Wyłączony", + "online": "Włączony", + "players": "Gracze", + "serverStarted": "Włączono serwer", + "serverStatus": "Status serwera", + "serverTime": "Czas UTC", + "serverTimeZone": "Strefa czasowa serwera", + "serverUptime": "Server Uptime", + "starting": "Opóźniony-Start", + "unableToConnect": "Nie można połączyć", + "version": "Wersja" + }, + "serverTerm": { + "commandInput": "Wpisz swoją komendę", + "delay-explained": "Crafty niedawno się odpalił, właczanie serwera będzie trochę opóźnione", + "downloading": "Pobieranie...", + "restart": "Restart", + "sendCommand": "Wyślij komendę", + "start": "Start", + "starting": "Opóźniony-Start", + "stop": "Zatrzymaj", + "stopScroll": "Zatrzymaj Auto-przewijanie", + "updating": "Aktualizowanie..." + }, + "serverWizard": { + "absoluteServerPath": "Pełna ścieżka serwera", + "absoluteZipPath": "Pełna ścieżka serwera", + "addRole": "Dodaj serwer do ról/roli", + "autoCreate": " Jeśli żadnej nie zaznaczysz, crafty stworzy nową!", + "bePatient": "Proszę, poczekaj kiedy ' + (importing ? 'importujemy' : 'pobieramy') + ' serwer", + "buildServer": "Zbuduj serwer!", + "clickRoot": "Kilknij tutaj aby zaznaczyć główną ścieżkę", + "close": "Zamknij", + "defaultPort": "25565 podstawowy", + "downloading": "Pobieranie serwera...", + "explainRoot": "Proszę, kliknij przycisk poniżej aby zaznaczyć główną ścieżkę w tym archiwum", + "importing": "Importowanie serwera...", + "importServer": "Importuj egzystujący serwer", + "importServerButton": "Importuj serwer!", + "importZip": "Importuj z ZIPa", + "maxMem": "Maks. RAMu", + "minMem": "Min. RAMu", + "myNewServer": "Mój nowy serwer", + "newServer": "Stwórz nowy serwer", + "quickSettings": "Szybkie ustawienia", + "quickSettingsDescription": "Nie martw się, możesz te ustawienia zmienić później", + "resetForm": "Resetuj formę", + "save": "Zapisz", + "selectRole": "Zaznacz rolę (lub kilka)", + "selectRoot": "Wybierz archiwum głównej ścieżki | (TBF, Select Archive Root Dir)", + "selectType": "Wybierz typ serwera", + "selectVersion": "Wybiesz wersje", + "selectZipDir": "Wybierz ścieżkę w archiwóm którą chcesz abyśmy wypakowali", + "serverJar": "Plik egzekucyjny serwera", + "serverName": "Nazwa serwera", + "serverPath": "Ścieżka serwera", + "serverPort": "Port serwera", + "serverType": "Typ serwera", + "serverVersion": "Wersja serwera", + "sizeInGB": "Wielkość w GB", + "zipPath": "Server Path" + }, + "sidebar": { + "contribute": "Współpracuj", + "credits": "Podziękowania", + "dashboard": "Panel", + "documentation": "Dokumentacja", + "navigation": "Nawigacja", + "newServer": "Stwórz nowy serwer", + "servers": "Serwery" + }, + "userConfig": { + "apiKey": "Klucze API", + "auth": "Autoryzacja? ", + "config": "Ustawienia", + "configArea": "Ustawienia użytkownika", + "configAreaDesc": "Tutaj możesz zmienić ustawienia użytkownika", + "confirmDelete": "Jesteś pewien że chcesz usunąć tego użytkownika ? To jest nieodwracalne.", + "craftyPermDesc": "Ten użytkownik ma dostęp do ustawień Craftiego takich jak: ", + "craftyPerms": "Ustawienia Craftiego: ", + "created": "Stworzono: ", + "deleteUser": "Usuń użytkownika: ", + "deleteUserB": "Usuń użytkownika", + "delSuper": "Nie możesz usunąć SuperUżytkownika", + "enabled": "Enabled", + "gravDesc": "To jest email tylko używany do Gravatar™. Crafty nie użyje pod żadnymi okolicznościami twojego adresu email, niż sprawdzenie ikony Gravatar™ ", + "gravEmail": "Gravatar™ Email", + "lastIP": "Ostatnie IP: ", + "lastLogin": "Ostatni login: ", + "lastUpdate": "Ostatni update: ", + "leaveBlank": "Jeśli chcesz zmienić użytkownika bez zmieniania hasła, nic nie wpisuj.", + "member": "Użytkownik?", + "notExist": "Nie możesz usunąć czegoś, co nie istnieje!", + "pageTitle": "Edytuj użytkownika", + "pageTitleNew": "Stwórz użytkownika", + "password": "Nowe hasło", + "permName": "Nazwa permisji", + "repeat": "Powtórz hasło", + "roleName": "Nazwa roli", + "super": "SuperUżytkownik", + "userLang": "Język użytkownika", + "userName": "Nazwa użytkownika", + "userNameDesc": "Jak chcesz nazwać tego użytkownika?", + "userRoles": "Role użytkownika", + "userRolesDesc": "Role, które ten użytkownik posiada.", + "userSettings": "Ustawienia użytkownika", + "uses": "Ilość użyć (-1==Bez limitu)" + } +} diff --git a/main.py b/main.py index 45fd67f2..f41c1d74 100644 --- a/main.py +++ b/main.py @@ -7,8 +7,9 @@ import argparse import logging.config import signal import peewee -from app.classes.shared.file_helpers import FileHelpers +from packaging import version as pkg_version +from app.classes.shared.file_helpers import FileHelpers from app.classes.shared.import3 import Import3 from app.classes.shared.console import Console from app.classes.shared.helpers import Helpers @@ -225,6 +226,19 @@ if __name__ == "__main__": controller_setup_thread.join() Console.info("Crafty has fully started and is now ready for use!") + + # Check if new version available + remote_ver = helper.check_remote_version() + if remote_ver: + notice = f""" + A new version of Crafty is available! + {'/' * 37} + New version available: {remote_ver} + Current version: {pkg_version.parse(helper.get_version_string())} + {'/' * 37} + """ + Console.yellow(notice) + crafty_prompt.prompt = f"Crafty Controller v{helper.get_version_string()} > " try: logger.info("Removing old temp dirs")