Merge branch 'dev' into 'master'

v4.0.19

See merge request crafty-controller/crafty-4!528
This commit is contained in:
Iain Powrie 2023-01-07 18:33:57 +00:00
commit 932ca8613a
27 changed files with 1161 additions and 338 deletions

View File

@ -29,6 +29,7 @@ docker-build-dev:
- docker run --rm --privileged aptman/qus -- -r
- docker run --rm --privileged aptman/qus -s -- -p aarch64 x86_64
- echo $CI_BUILD_TOKEN | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
- echo $DOCKERHUB_TOKEN | docker login -u "$DOCKERHUB_USER" --password-stdin $DOCKERHUB_REGISTRY
script:
- |
tag=":$CI_COMMIT_REF_SLUG"
@ -45,6 +46,7 @@ docker-build-dev:
--build-arg "BUILD_REF=${CI_COMMIT_SHA}"
--build-arg "CRAFTY_VER=${VERSION}"
--tag "$CI_REGISTRY_IMAGE${tag}"
--tag "arcadiatechnology/crafty-4${tag}"
--platform linux/arm64/v8,linux/amd64
--push .
after_script:
@ -83,6 +85,7 @@ docker-build-prod:
- docker run --rm --privileged aptman/qus -- -r
- docker run --rm --privileged aptman/qus -s -- -p aarch64 x86_64
- echo $CI_BUILD_TOKEN | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
- echo $DOCKERHUB_TOKEN | docker login -u "$DOCKERHUB_USER" --password-stdin $DOCKERHUB_REGISTRY
script:
- |
VERSION="${MAJOR}.${MINOR}.${SUB}"
@ -99,6 +102,8 @@ docker-build-prod:
--build-arg "CRAFTY_VER=${VERSION}"
--tag "$CI_REGISTRY_IMAGE:$VERSION"
--tag "$CI_REGISTRY_IMAGE:latest"
--tag "arcadiatechnology/crafty-4:$VERSION"
--tag "arcadiatechnology/crafty-4:latest"
--platform linux/arm64/v8,linux/amd64
--push .
after_script:

View File

@ -1,4 +1,29 @@
# Changelog
## --- [4.0.19] - 2022/01/07
### Bug fixes
- Fix port tooltip not showing on dash while server online. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/503))
- Fix '+' char in path causing any file operation to fail. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/502))
- Fix colours on public pages. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/504))
- Fix bug where public background was not sent to public pages...like the error page resulting in an error...ironic...I know. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/505))
- Be sure a user cannot server import crafty dir. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/506))
- Remove Pathlib from sub path check ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/507))
- Fix root dir selection in Upload Zip Import ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/508))
- Fix stats error on mac M1 chips ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/512))
- Fix window path escape on java override ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/513))
- Fix Forge import stalling on 1.17 Forge servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/515))
- Fix issue with server config for SU Accounts ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/516))
- Fix Nested reaction tasks ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/521))
- Remove legacy unzip code causing issues with single file zip files ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/522))
### Tweaks
- Make server directories non-configurable ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/511))
- Add popover to server port to detail it's purpose ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/514))
- Add server start timeout w/ WS Warning ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/518))
- Replace google ping for ntp for internet checks in locked-down countries ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/524))
- Add pushing to DockerHub registry (`arcadiatechnology/crafty-4`) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/526))
### Lang
- Added Czech translation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/519))
<br><br>
## --- [4.0.17/4.0.18] - 2022/11/30
### New features
- Automate forge install process through Crafty server creation for Forge server version 1.16 and greater. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/495))

View File

@ -2,7 +2,7 @@
*Don't Panic!*<br><br>
First off, thank you for choosing Crafty Controller! <br>
We hope you've been enjoying the beta so far and are absolutely thrilled that you are looking to contribute!
We hope you've been enjoying Crafty so far and are absolutely thrilled that you are looking to contribute!
The following guide will show you how to easily and safely contribute to our current workflow. There are a few components that need to be taken into account and processes that need followed before we can merge your code into our repository.
<br><br>

View File

@ -1,5 +1,5 @@
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
# Crafty Controller 4.0.18
# Crafty Controller 4.0.19
> Python based Control Panel for your Minecraft Server
## What is Crafty Controller?
@ -48,7 +48,7 @@ As the Dockerfile uses the permission structure of `crafty:root` **internally**
### - Using the registry image 🌎
The provided image supports both `arm64` and `amd64` out the box, if you have issues though you can build it yourself with the `compose` file in `docker/`.
The image is located at: `registry.gitlab.com/crafty-controller/crafty-4:latest`
The image is located at: `registry.gitlab.com/crafty-controller/crafty-4:latest` or `arcadiatechnology/crafty-4`
| Branch | Status |
| ----------------- | ------------------------------------------------------------------ |
| :latest | [![pipeline status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master) |

View File

@ -192,7 +192,7 @@ class ServerJars:
with open(path, "wb") as output:
shutil.copyfileobj(r.raw, output)
# If this is the newer forge version we will run the installer
if server == "forge" and int(version.split(".")[1]) > 15:
if server == "forge":
ServersController.finish_import(server_id, True)
else:
ServersController.finish_import(server_id)

View File

@ -86,7 +86,7 @@ class Stats:
def get_node_stats(self) -> NodeStatsReturnDict:
try:
cpu_freq = psutil.cpu_freq()
except NotImplementedError:
except (NotImplementedError, FileNotFoundError):
cpu_freq = None
if cpu_freq is None:
cpu_freq = psutil._common.scpufreq(current=-1, min=-1, max=-1)

View File

@ -298,14 +298,7 @@ class FileHelpers:
try:
with zipfile.ZipFile(zip_path, "r") as zip_ref:
zip_ref.extractall(temp_dir)
for i in enumerate(zip_ref.filelist):
if len(zip_ref.filelist) > 1 or not zip_ref.filelist[
i
].filename.endswith("/"):
break
full_root_path = temp_dir
for item in os.listdir(full_root_path):
if os.path.isdir(os.path.join(full_root_path, item)):
try:

View File

@ -272,7 +272,7 @@ class Helpers:
@staticmethod
def check_internet():
try:
requests.get("https://google.com", timeout=1)
requests.get("https://ntp.org", timeout=1)
return True
except Exception:
return False
@ -376,6 +376,17 @@ class Helpers:
return default_return
@staticmethod
def is_subdir(server_path, root_dir):
server_path = os.path.realpath(server_path)
root_dir = os.path.realpath(root_dir)
relative = os.path.relpath(server_path, root_dir)
if relative.startswith(os.pardir):
return False
return True
def set_setting(self, key, new_value):
try:
with open(self.settings_file, "r", encoding="utf-8") as f:

View File

@ -815,6 +815,7 @@ class Controller:
user_id,
server_type="minecraft-bedrock",
)
ServersController.set_import(new_id)
self.import_helper.import_bedrock_zip_server(
temp_dir, new_server_dir, full_jar_path, port, new_id
)

View File

@ -10,6 +10,7 @@ import logging.config
import subprocess
import html
import urllib.request
import glob
# TZLocal is set as a hidden import on win pipeline
from tzlocal import get_localzone
@ -580,13 +581,59 @@ class ServerInstance:
# Process has exited. Lets do some work to setup the new
# run command.
# Let's grab the server object we're going to update.
server_obj = HelperServers.get_server_obj(self.server_id)
server_obj: Servers = HelperServers.get_server_obj(self.server_id)
# The forge install is done so we can delete that install file.
os.remove(os.path.join(server_obj.path, server_obj.executable))
# We need to grab the exact forge version number.
# We know we can find it here in the run.sh/bat script.
try:
# Getting the forge version from the executable command
version = re.findall(
r"forge-([0-9\.]+)((?:)|(?:-([0-9\.]+)-[a-zA-Z]+)).jar",
server_obj.execution_command,
)
version_param = version[0][0].split(".")
version_major = int(version_param[0])
version_minor = int(version_param[1])
# Checking which version we are with
if version_major <= 1 and version_minor < 17:
# OLD VERSION < 1.17
# Retrieving the executable jar filename
file_path = glob.glob(
f"{server_obj.path}/forge-{version[0][0]}*.jar"
)[0]
file_name = re.findall(
r"(forge[-0-9.]+.jar)",
file_path,
)[0]
# Let's set the proper server executable
server_obj.executable = os.path.join(file_name)
# Get memory values
memory_values = re.findall(
r"-Xms([A-Z0-9\.]+) -Xmx([A-Z0-9\.]+)",
server_obj.execution_command,
)
# Now lets set up the new run command.
# This is based off the run.sh/bat that
# Forge uses in 1.17 and <
execution_command = (
f"java -Xms{memory_values[0][0]} -Xmx{memory_values[0][1]}"
f' -jar "{file_name}" nogui'
)
server_obj.execution_command = execution_command
Console.debug("SUCCESS! Forge install completed")
else:
# NEW VERSION >= 1.17
run_file_path = ""
if self.helper.is_os_windows():
run_file_path = os.path.join(server_obj.path, "run.bat")
@ -608,7 +655,8 @@ class ServerInstance:
# We get the server command parameters from forge script
server_command = re.findall(
r"java @([a-zA-Z0-9_\.]+)"
r" @([a-z.\/\-]+)([0-9.\-]+)\/\b([a-z_0-9]+\.txt)\b( .{2,4})?",
r" @([a-z.\/\-]+)([0-9.\-]+)"
r"\/\b([a-z_0-9]+\.txt)\b( .{2,4})?",
run_file_text,
)[0]
@ -621,13 +669,17 @@ class ServerInstance:
)
# Now lets set up the new run command.
# This is based off the run.sh/bat that
# Forge uses in 1.16 and <
# Forge uses in 1.17 and <
execution_command = (
f"java @{server_command[0]}"
f" @{executable_path}{server_command[3]} nogui {server_command[4]}"
f" @{executable_path}{server_command[3]} nogui"
" {server_command[4]}"
)
server_obj.execution_command = execution_command
Console.debug("SUCCESS! Forge install completed")
except:
logger.debug("Could not find run file.")
# TODO Use regex to get version and rebuild simple execution
# We'll update the server with the new information now.
HelperServers.update_server(server_obj)

View File

@ -556,7 +556,7 @@ class TasksManager:
if task.one_time:
self.remove_job(task.schedule_id)
logger.info("one time task detected. Deleting...")
else:
elif task.interval_type != "reaction":
self.controller.management.update_scheduled_task(
task.schedule_id,
{

View File

@ -4,6 +4,7 @@ import pathlib
import re
import logging
import time
import urllib.parse
import bleach
import tornado.web
import tornado.escape
@ -507,12 +508,12 @@ class AjaxHandler(BaseHandler):
self.redirect("/panel/dashboard")
elif page == "unzip_server":
path = self.get_argument("path", None)
path = urllib.parse.unquote(self.get_argument("path", ""))
if not path:
path = os.path.join(
self.controller.project_root,
"imports",
self.get_argument("file", ""),
urllib.parse.unquote(self.get_argument("file", "")),
)
if Helpers.check_file_exists(path):
self.helper.unzip_server(path, exec_user["user_id"])

View File

@ -7,6 +7,7 @@ import json
import logging
import threading
import shlex
import urllib.parse
import bleach
import requests
import tornado.web
@ -289,6 +290,7 @@ class PanelHandler(BaseHandler):
page_data: t.Dict[str, t.Any] = {
# todo: make this actually pull and compare version data
"update_available": self.helper.update_available,
"background": self.controller.cached_login,
"serverTZ": tz,
"version_data": self.helper.get_version_string(),
"failed_servers": self.controller.servers.failed_servers,
@ -806,9 +808,15 @@ class PanelHandler(BaseHandler):
user_roles_list = self.controller.users.get_user_roles_names(
user.user_id
)
try:
user_servers = self.controller.servers.get_authorized_servers(
user.user_id
)
except:
return self.redirect(
"/panel/error?error=Cannot load panel config"
" while servers are unloaded"
)
servers = []
for server in user_servers:
if server.name not in servers:
@ -1386,9 +1394,10 @@ class PanelHandler(BaseHandler):
template = "panel/activity_logs.html"
elif page == "download_file":
file = Helpers.get_os_understandable_path(self.get_argument("path", ""))
name = self.get_argument("name", "")
file = Helpers.get_os_understandable_path(
urllib.parse.unquote(self.get_argument("path", ""))
)
name = urllib.parse.unquote(self.get_argument("name", ""))
server_id = self.check_server_id()
if server_id is None:
return
@ -1512,10 +1521,6 @@ class PanelHandler(BaseHandler):
server_obj = self.controller.servers.get_server_obj(server_id)
shutdown_timeout = self.get_argument("shutdown_timeout", 60)
if superuser:
server_path = self.get_argument("server_path", None)
if Helpers.is_os_windows():
server_path.replace(" ", "^ ")
server_path = Helpers.wtol_path(server_path)
log_path = self.get_argument("log_path", "")
if log_path:
if Helpers.is_os_windows():
@ -1551,7 +1556,10 @@ class PanelHandler(BaseHandler):
return
if java_selection:
try:
execution_list = shlex.split(execution_command)
if self.helper.is_os_windows():
execution_list = shlex.split(execution_command, posix=False)
else:
execution_list = shlex.split(execution_command, posix=True)
except ValueError:
self.redirect(
"/panel/error?error=Invalid execution command. Java path"
@ -1601,9 +1609,8 @@ class PanelHandler(BaseHandler):
server_obj.shutdown_timeout = shutdown_timeout
if superuser:
if Helpers.validate_traversal(
self.helper.get_servers_root_dir(), server_path
self.helper.get_servers_root_dir(), server_obj.path
):
server_obj.path = server_path
server_obj.log_path = log_path
if Helpers.validate_traversal(
self.helper.get_servers_root_dir(), executable
@ -1615,7 +1622,6 @@ class PanelHandler(BaseHandler):
server_obj.executable_update_url = executable_update_url
server_obj.show_status = show_status
else:
server_obj.path = server_obj.path
server_obj.log_path = server_obj.log_path
server_obj.executable = server_obj.executable
server_obj.execution_command = execution_command

View File

@ -90,6 +90,7 @@ class ApiServersServerIndexHandler(BaseApiHandler):
server_obj = self.controller.servers.get_server_obj(server_id)
for key in data:
# If we don't validate the input there could be security issues
if key != "path":
setattr(server_obj, key, data[key])
self.controller.servers.update_server(server_obj)

View File

@ -331,6 +331,15 @@ class ServerHandler(BaseHandler):
return
if import_type == "import_jar":
if not self.helper.is_subdir(
import_server_path, self.controller.project_root
):
self.redirect(
"/panel/error?error=Loop Error: The selected path will cause"
" an infinite copy loop. Make sure Crafty's directory is not"
" in your server path."
)
return
good_path = self.controller.verify_jar_server(
import_server_path, import_server_jar
)
@ -480,6 +489,15 @@ class ServerHandler(BaseHandler):
return
if import_type == "import_jar":
if self.helper.is_subdir(
import_server_path, self.controller.project_root
):
self.redirect(
"/panel/error?error=Loop Error: The selected path will cause"
" an infinite copy loop. Make sure Crafty's directory is not"
" in your server path."
)
return
good_path = self.controller.verify_jar_server(
import_server_path, import_server_exe
)

View File

@ -1,6 +1,7 @@
import logging
import os
import time
import urllib.parse
import tornado.web
import tornado.options
import tornado.httpserver
@ -108,7 +109,9 @@ class UploadHandler(BaseHandler):
logger.debug("Could not delete file on user server upload")
self.helper.ensure_dir_exists(path)
filename = self.request.headers.get("X-FileName", None)
filename = urllib.parse.unquote(
self.request.headers.get("X-FileName", None)
)
if not str(filename).endswith(".zip"):
self.helper.websocket_helper.broadcast("close_upload_box", "error")
self.finish("error")

View File

@ -1,5 +1,5 @@
{
"major": 4,
"minor": 0,
"sub": 18
"sub": 19
}

View File

@ -22155,7 +22155,7 @@ ul li {
}
.popover .popover-body {
color: #000;
color: var(--base-text);
background: var(--card-banner-bg);
}

View File

@ -278,9 +278,10 @@
{% end %}
</td>
<td draggable="false" id="server_running_status_{{server['server_data']['server_id']}}">
<td draggable="false">
<span class="port" data-toggle="tooltip" title="{{
server['server_data']['server_port'] }}">
<div id="server_running_status_{{server['server_data']['server_id']}}">
{% if server['stats']['running'] %}
<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online',
data['lang']) }}</span>
@ -299,6 +300,7 @@
data-players="{{ server['stats']['online']}}" data-max="{{ server['stats']['max'] }}"></span>
</tr>
{% end %}
</div>
</span>
{% for server in data['failed_servers'] %}
<tr id="{{server['server_id']}}" draggable="false">
@ -536,7 +538,7 @@
</div>
</div>
</div>
</div>
</div>
</div>
@ -604,7 +606,6 @@
function send_command(server_id, command) {
/* this getCookie function is in base.html */
const token = getCookie("_xsrf");
$.ajax({
type: "POST",
headers: { 'X-XSRFToken': token },
@ -621,6 +622,51 @@
});
}
function warn(message, link = null, className = null) {
var closeEl = document.createElement('span');
var strongEL = document.createElement('strong');
var msgEl = document.createElement('div');
closeEl.innerHTML = '&times;';
strongEL.textContent = 'Warning: ';
msgEl.append(strongEL, message);
closeEl.style.marginLeft = '15px';
closeEl.style.fontWeight = 'bold';
closeEl.style.float = 'right';
closeEl.style.fontSize = '22px';
closeEl.style.lineHeight = '20px';
closeEl.style.cursor = 'pointer';
closeEl.addEventListener('click', function () { this.parentElement.style.display = 'none'; });
var parentEl = document.createElement('div');
parentEl.style.padding = '20px';
parentEl.style.backgroundColor = '#f7970f';
parentEl.appendChild(closeEl);
parentEl.appendChild(msgEl);
if (link) {
let linkEl = document.createElement('a')
linkEl.href = link;
linkEl.innerHTML = "See our documentation for details.";
linkEl.style.color = 'white';
linkEl.style.textDecoration = 'underline';
linkEl.target = "_blank";
parentEl.appendChild(linkEl);
}
if (className) {
parentEl.classList.add(className);
}
document.querySelector('.dynamicMsg').appendChild(parentEl);
}
function send_kill(server_id) {
/* this getCookie function is in base.html */
const token = getCookie("_xsrf");
@ -772,11 +818,15 @@
send_command(server_id, 'start_server');
bootbox.alert({
backdrop: true,
title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}',
title: '<span class="dynamicMsg">{% raw translate("dashboard", "sendingCommand", data["lang"]) %}</span>',
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; {% raw translate("dashboard", "bePatientStart", data["lang"]) %} </div>'
});
setTimeout(finishTimeout, 60000);
});
function finishTimeout() {
warn("It seems this is taking a while...it's possible you're using UBlock or a similar ad blocker and it's causing some of our connections to not make it to the server. Try disabling your ad blocker.",
null, 'wssError');
}
$(".stop_button").click(function () {
console.log("stopping server");
server_id = $(this).attr("data-id");

View File

@ -62,9 +62,10 @@
<label for="server_path">{{ translate('serverConfig', 'serverPath', data['lang']) }} <small
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverPathDesc', data['lang']) }}</small>
</label>
<input type="text" class="form-control" name="server_path" id="server_path"
value="{{ data['server_stats']['server_id']['path'] }}"
placeholder="{{ translate('serverConfig', 'serverPath', data['lang']) }}" required>
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<span style="color: gray; font-size: 12px;">{{ data['server_stats']['server_id']['path'] }}</span>
🔒
</div>
</div>
{% if data['server_stats']['server_type'] != "minecraft-bedrock" %}
@ -157,7 +158,6 @@
<input type="text" class="form-control" name="server_ip" id="server_ip"
value="{{ data['server_stats']['server_id']['server_ip'] }}" required>
</div>
<div class="form-group">
<label for="server_port">{{ translate('serverConfig', 'serverPort', data['lang']) }} <small
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverPortDesc', data['lang']) }}
@ -165,6 +165,11 @@
<input type="number" class="form-control" name="server_port" id="server_port"
value="{{ data['server_stats']['server_id']['server_port'] }}" step="1" max="65566" min="1"
required>
<span data-html="true" class="port-hint text-center"
title="<i class='fal fa-exclamation-triangle'></i> " ,
data-content="{{
translate('serverConfig', 'statsHint1' , data['lang'])}} <br> <br> <strong>{{ translate('serverConfig', 'statsHint2', data['lang'])}}</strong>" ,
data-placement="right"></span>
</div>
{% end %}
@ -522,6 +527,20 @@
});
}
$("#server_port").focus(function () {
$('[data-toggle="popover"]').popover();
if ($(window).width() < 1000) {
$('.port-hint').attr("data-placement", "top")
} else {
$('.port-hint').attr("data-placement", "right")
}
$('.port-hint').popover("show");
});
$("#server_port").focusout(function () {
$('.port-hint').popover("hide");
});
$(document).ready(function () {
webSocket.on('remove_spinner', function () {
document.getElementById("update-spinner").style.visibility = "hidden";

View File

@ -1027,7 +1027,9 @@
function downloadFileE(event) {
path = event.target.parentElement.getAttribute('data-path');
name = event.target.parentElement.getAttribute('data-name');
window.location.href = `/panel/download_file?id=${serverId}&path=${path}&name=${name}`;
encoded_path = encodeURIComponent(path)
encoded_name = encodeURIComponent(name)
window.location.href = `/panel/download_file?id=${serverId}&path=${encoded_path}&name=${encoded_name}`;
}
function renameItemE(event) {

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="{{ data['lang_page'] }}">
<html lang="{{ data['lang_page'] }}" class="default">
<head>
<!-- Required meta tags -->
@ -24,7 +24,7 @@
<link rel="alternate icon" href="/static/assets/images/favicon.png" />
</head>
<body class="dark-theme">
<body>
<div class="container-scroller">
<div class="container-fluid page-body-wrapper full-page-wrapper">
<div class="content-wrapper d-flex align-items-sm-center auth auth-bg-1 theme-one">

View File

@ -424,8 +424,8 @@
<input type="text" class="form-control" id="zip_root_path" name="zip_root_path">
</div>
</div>
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select"
aria-hidden="true">
<div class="modal fade" id="dir_upload_select" tabindex="-1" role="dialog"
aria-labelledby="dir_select" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
@ -436,7 +436,7 @@
</button>
</div>
<div class="modal-body">
<div class="tree-ctx-item" id="main-tree-div" data-path=""
<div class="tree-ctx-item" id="main-tree-div-upload" data-path=""
style="overflow: scroll; max-height:75%;">
<input type="radio" id="main-tree-input-upload" name="root_path" value="" checked>
<span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path="">
@ -558,14 +558,14 @@
{% block js%}
<script>
var upload;
var file;
function sendFile() {
file = $("#file")[0].files[0]
document.getElementById("upload_input").innerHTML = '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%">&nbsp;<i class="fa-solid fa-spinner"></i></div></div>'
let xmlHttpRequest = new XMLHttpRequest();
let token = getCookie("_xsrf")
let fileName = file.name
let fileName = encodeURIComponent(file.name)
let target = '/upload'
let mimeType = file.type
let size = file.size
@ -597,8 +597,9 @@
document.getElementById("root_upload_button").addEventListener("click", function () {
if (file) {
upload = true;
if (document.getElementById('root_upload_button').classList.contains('clicked')) {
document.getElementById('main-tree-div').innerHTML = '<input type="radio" id="main-tree-input-upload" 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><i class="far fa-folder-open"></i>{{ translate("serverFiles", "files", data["lang"]) }}</span></input>'
document.getElementById('main-tree-div-upload').innerHTML = '<input type="radio" id="main-tree-input-upload" name="root_path" value="" checked><span id="main-tree-upload" class="files-tree-title tree-caret-down root-dir" data-path=""><i class="far fa-folder"></i><i class="far fa-folder-open"></i>{{ translate("serverFiles", "files", data["lang"]) }}</span></input>'
} else {
document.getElementById('root_upload_button').classList.add('clicked')
}
@ -610,7 +611,7 @@
$.ajax({
type: "POST",
headers: { 'X-XSRFToken': token },
url: '/ajax/unzip_server?id=-1&file=' + file.name,
url: '/ajax/unzip_server?id=-1&file=' + encodeURIComponent(file.name),
});
} else {
bootbox.alert("You must input a path before selecting this button");
@ -663,7 +664,7 @@
$.ajax({
type: "POST",
headers: { 'X-XSRFToken': token },
url: '/ajax/unzip_server?id=-1&path=' + path,
url: '/ajax/unzip_server?id=-1&path=' + encodeURIComponent(path),
});
} else {
bootbox.alert("You must input a path before selecting this button");
@ -692,8 +693,12 @@
}
function show_file_tree() {
if (upload) {
$("#dir_upload_select").modal();
} else {
$("#dir_select").modal();
}
}
function getTreeView(path) {
const styles = window.getComputedStyle(document.getElementById("lower_half"));
@ -716,13 +721,21 @@
dataArr = data.split('\n');
serverDir = dataArr.shift(); // Remove & return first element (server directory)
text = dataArr.join('\n');
if (styles.visibility === "hidden") {
try {
document.getElementById('main-tree-div').innerHTML += text;
document.getElementById('main-tree').parentElement.classList.add("clicked");
} catch {
document.getElementById('files-tree').innerHTML = text;
}
} else {
try {
document.getElementById('main-tree-div-upload').innerHTML += text;
document.getElementById('main-tree-upload').parentElement.classList.add("clicked");
} catch {
document.getElementById('files-tree').innerHTML = text;
}
}
document.getElementsByClassName('files-tree-title')[0].setAttribute('data-path', serverDir);

View File

@ -552,8 +552,8 @@
<input type="text" class="form-control" id="zip_root_path" name="zip_root_path">
</div>
</div>
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select"
aria-hidden="true">
<div class="modal fade" id="dir_upload_select" tabindex="-1" role="dialog"
aria-labelledby="dir_select" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
@ -564,10 +564,11 @@
</button>
</div>
<div class="modal-body">
<div class="tree-ctx-item" id="main-tree-div" data-path=""
<div class="tree-ctx-item" id="main-tree-div-upload" data-path=""
style="overflow: scroll; max-height:75%;">
<input type="radio" id="main-tree-input-upload" name="root_path" value="" checked>
<span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path="">
<span id="main-tree-upload" class="files-tree-title tree-caret-down root-dir"
data-path="">
<i class="far fa-folder"></i>
<i class="far fa-folder-open"></i>
{{ translate('serverFiles', 'files', data['lang']) }}
@ -788,7 +789,7 @@
$.ajax({
type: "POST",
headers: { 'X-XSRFToken': token },
url: '/ajax/unzip_server?id=-1&path=' + path,
url: '/ajax/unzip_server?id=-1&path=' + encodeURIComponent(path),
});
} else {
bootbox.alert("You must input a path before selecting this button");
@ -799,6 +800,7 @@
{% block js %}
<script>
var upload = false;
var file;
function sendFile() {
file = $("#file")[0].files[0]
@ -853,7 +855,7 @@
$.ajax({
type: "POST",
headers: { 'X-XSRFToken': token },
url: '/ajax/unzip_server?id=-1&path=' + path,
url: '/ajax/unzip_server?id=-1&path=' + encodeURIComponent(path),
});
} else {
bootbox.alert("You must input a path before selecting this button");
@ -862,8 +864,9 @@
document.getElementById("root_upload_button").addEventListener("click", function () {
if (file) {
upload = true;
if (document.getElementById('root_upload_button').classList.contains('clicked')) {
document.getElementById('main-tree-div').innerHTML = '<input type="radio" id="main-tree-input-upload" 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><i class="far fa-folder-open"></i>{{ translate("serverFiles", "files", data["lang"]) }}</span></input>'
document.getElementById('main-tree-div-upload').innerHTML = '<input type="radio" id="main-tree-input-upload" name="root_path" value="" checked><span id="main-tree-upload" class="files-tree-title tree-caret-down root-dir" data-path=""><i class="far fa-folder"></i><i class="far fa-folder-open"></i>{{ translate("serverFiles", "files", data["lang"]) }}</span></input>'
} else {
document.getElementById('root_upload_button').classList.add('clicked')
}
@ -875,7 +878,7 @@
$.ajax({
type: "POST",
headers: { 'X-XSRFToken': token },
url: '/ajax/unzip_server?id=-1&file=' + file.name,
url: '/ajax/unzip_server?id=-1&file=' + encodeURIComponent(file.name),
});
} else {
bootbox.alert("You must input a path before selecting this button");
@ -926,8 +929,12 @@
}
function show_file_tree() {
if (upload) {
$("#dir_upload_select").modal();
} else {
$("#dir_select").modal();
}
}
function check_sizes(a, b, changed) {
max_mem = parseFloat(a.val());
@ -961,14 +968,21 @@
dataArr = data.split('\n');
serverDir = dataArr.shift(); // Remove & return first element (server directory)
text = dataArr.join('\n');
if (styles.visibility === "hidden") {
try {
document.getElementById('main-tree-div').innerHTML += text;
document.getElementById('main-tree').parentElement.classList.add("clicked");
} catch {
document.getElementById('files-tree').innerHTML = text;
}
} else {
try {
document.getElementById('main-tree-div-upload').innerHTML += text;
document.getElementById('main-tree-upload').parentElement.classList.add("clicked");
} catch {
document.getElementById('files-tree').innerHTML = text;
}
}
document.getElementsByClassName('files-tree-title')[0].setAttribute('data-path', serverDir);
document.getElementsByClassName('files-tree-title')[0].setAttribute('data-name', 'Files');

606
app/translations/cs_CS.json Normal file
View File

@ -0,0 +1,606 @@
{
"404": {
"contact": "Kontaktujte podporu Crafty přes Discord",
"notFound": "Stránka nebyla nalezena",
"unableToFind": "Hledanou stránku se nám nepodařilo najít. Zkuste to prosím znovu nebo se vraťte a obnovte stránku."
},
"accessDenied": {
"accessDenied": "Přístup odepřen",
"contact": "Kontaktujte podporu Crafty přes Discord",
"contactAdmin": "Pro přístup k tomuto prostředku se obraťte na správce serveru, nebo pokud si myslíte, že byste k němu měli mít přístup, kontaktujte podporu.",
"noAccess": "K tomuto zdroji nemáte přístup"
},
"apiKeys": {
"apiKeys": "Klíče API",
"auth": "Autorizován? ",
"buttons": "Tlačítka",
"config": "Nastavení",
"crafty": "Crafty: ",
"created": "Vytvořen",
"createNew": "Vytvořit nový token API",
"deleteKeyConfirmation": "Chcete tento API klíč odstranit? Tuto akci nelze vrátit zpět.",
"deleteKeyConfirmationTitle": "Odstranit klíč API ${keyId}?",
"getToken": "Získat token",
"name": "Jméno",
"nameDesc": "Jak chcete nazvat tento token API? ",
"no": "Ne",
"pageTitle": "Úprava uživatelských klíčů API",
"permName": "Název oprávnění",
"perms": "Oprávnění",
"server": "Server: ",
"superUser": "Super uživatel",
"yes": "Ano"
},
"base": {
"doesNotWorkWithoutJavascript": "<strong>Varování: </strong>Crafty nefunguje správně, pokud není povolen JavaScript!"
},
"credits": {
"developmentTeam": "Vývojový tým",
"hugeDesc": "Neskutečně",
"pageDescription": "Bez těchto lidí bychom neměli Crafty.",
"pageTitle": "Zásluhy",
"patreonDesc": "našim příznivcům Patreonu / Ko-fi!",
"patreonOther": "Další",
"patreonSupporter": "Podpůrci Patreonu / Ko-fi",
"patreonUpdate": "Poslední aktualizace:",
"retiredStaff": "Bývalí zaměstnanci",
"subscriberName": "Jméno",
"subscriptionLevel": "Úroveň",
"supportTeam": "Tým podpory a dokumentace",
"thankYou": "DĚKUJEME",
"translationDesc": "naší komunitě, která překládá! [ Aktivní = 🟢 Neaktivní/ukončený = ⚪ ]",
"translationName": "Jazyk",
"translationTitle": "Překlad do jazyků",
"translator": "Překladatelé"
},
"dashboard": {
"actions": "Akce",
"allServers": "Všechny servery",
"avg": "Průměr",
"backups": "Zálohy",
"bePatientClone": "Buďte prosím trpěliví, než se dokončí klonování serveru.<br /> Tato obrazovka se za okamžik aktualizuje",
"bePatientRestart": "Buďte prosím trpěliví, než se dokončí restart serveru.<br /> Tato obrazovka se za okamžik aktualizuje",
"bePatientStart": "Buďte prosím trpěliví, než se dokončí spuštění serveru.<br /> Tato obrazovka se za okamžik aktualizuje",
"bePatientStop": "Buďte prosím trpěliví, než se dokončí zastavení serveru.<br /> Tato obrazovka se za okamžik aktualizuje",
"cannotSee": "Nezobrazuje se vám vše?",
"cannotSeeOnMobile": "Nezobrazuje se vám vše na mobilu?",
"cannotSeeOnMobile2": "Zkuste posunout tabulku do strany.",
"clone": "Klon",
"cloneConfirm": "Opravdu chcete tento server naklonovat? Tento proces může chvíli trvat.",
"cpuCores": "Jádra CPU",
"cpuCurFreq": "Aktuální takt CPU",
"cpuMaxFreq": "Maximální takt CPU",
"cpuUsage": "Využití CPU",
"crashed": "Crashnuté",
"dashboard": "Ovládací panel",
"delay-explained": "Služba/agent byla nedávno spuštěna a zpožďuje spuštění instance minecraft serveru.",
"host": "Hostitel",
"kill": "Zabít proces",
"killing": "Zabíjím proces...",
"lastBackup": "Poslední:",
"max": "Max",
"memUsage": "Využití paměti",
"motd": "MOTD",
"newServer": "Vytvořit nový server",
"nextBackup": "Další:",
"no-servers": "V současné době nejsou k dispozici žádné servery. Chcete-li začít, klikněte na",
"offline": "Offline",
"online": "Online",
"players": "Hráči",
"restart": "Restartovat",
"sendingCommand": "Odeslání příkazu",
"server": "Server",
"servers": "Servery",
"size": "Velikost složky serveru",
"start": "Start",
"starting": "Zpožděný start",
"status": "Stav",
"stop": "Zastavit",
"version": "Verze",
"welcome": "Vítejte v Crafty Controlleru",
"installing": "Instalace..."
},
"datatables": {
"i18n": {
"aria": {
"sortAscending": ": aktivace řazení sloupce vzestupně",
"sortDescending": ": aktivace seřazení sloupce sestupně"
},
"buttons": {
"collection": "Sbírka <span class='ui-button-icon-primary ui-icon ui-icon-triangle-1-s'/>",
"colvis": "Viditelnost sloupců",
"colvisRestore": "Obnovit viditelnost",
"copy": "Kopírovat",
"copyKeys": "Stisknutím kláves Ctrl nebo u2318 + C zkopírujete data tabulky do systémové schránky.<br><br>Chcete-li tuto zprávu zrušit, klikněte na ni nebo stiskněte klávesu ESC.",
"copySuccess": {
"1": "Zkopírován 1 řádek do schránky",
"2": "Zkopírovány 2 řádky do schránky",
"3": "Zkopírovány 3 řádky do schránky",
"4": "Zkopírovány 4 řádky do schránky",
"_": "Zkopírováno %d řádků do schránky"
},
"copyTitle": "Zkopírovat do schránky",
"csv": "CSV",
"excel": "Excel",
"pageLength": {
"1": "Zobrazit 1 řádek",
"2": "Zobrazit 2 řádky",
"3": "Zobrazit 3 řádky",
"4": "Zobrazit 4 řádky",
"-1": "Zobrazit všechny řádky",
"_": "Zobrazit %d řádků"
},
"pdf": "PDF",
"print": "Tisk"
},
"decimal": "",
"emptyTable": "V tabulce nejsou k dispozici žádné údaje",
"info": "Zobrazeno _START_ až _END_ z _TOTAL_ záznamů",
"infoEmpty": "Zobrazeno 0 až 0 z 0 záznamů",
"infoFiltered": "(filtrováno z _MAX_ celkových záznamů)",
"infoPostFix": "",
"lengthMenu": "Zobrazit položky _MENU_",
"loadingRecords": "Načítání...",
"paginate": {
"first": "První",
"last": "Poslední",
"next": "Další",
"previous": "Předchozí"
},
"processing": "Zpracování...",
"search": "Hledat:",
"select": {
"cells": {
"0": "Kliknutím na buňku ji vyberete",
"1": "Vybraná %d buňka",
"2": "Vybrané %d buňky",
"3": "Vybrané %d buňky",
"4": "Vybrané %d buňky",
"_": "Vybráno %d buněk"
},
"columns": {
"0": "Kliknutím na sloupec jej vyberete",
"1": "Vybraný %d sloupec",
"2": "Vybrané %d sloupce",
"3": "Vybrané %d sloupce",
"4": "Vybrané %d sloupce",
"_": "Vybráno %d sloupců"
},
"rows": {
"0": "Kliknutím na řádek jej vyberete",
"1": "Vybraný %d řádek",
"2": "Vybrané %d řádky",
"3": "Vybrané %d řádky",
"4": "Vybrané %d řádky",
"_": "Vybráno %d řádků"
}
},
"thousands": " ",
"zeroRecords": "Nebyly nalezeny žádné odpovídající záznamy"
}
},
"error": {
"contact": "Kontaktujte podporu Crafty přes Discord",
"embarassing": "Ach jo, no, to je trapné.",
"error": "Chyba!",
"eulaAgree": "Souhlasíte?",
"eulaMsg": "Musíte souhlasit s ",
"privMsg": "a ",
"eulaTitle": "Souhlas s EULA",
"agree": "Souhlasím",
"cancel": "Zrušit",
"fileTooLarge": "Odeslání se nezdařilo. Příliš velký nahraný soubor. Obraťte se na správce systému.",
"hereIsTheError": "Zde je chyba",
"internet": "Zjistili jsme, že počítač se spuštěným programem Crafty není připojen k internetu. Připojení klientů k serveru může být omezeno.",
"no-file": "Zdá se, že nemůžeme najít požadovaný soubor. Překontrolujte cestu. Má Crafty správné oprávnění?",
"noJava": "Server {} se nepodařilo spustit s kódem chyby: Zjistili jsme, že Java není nainstalována. Nainstalujte prosím Javu a poté spusťte server.",
"not-downloaded": "Zdá se, že nemůžeme najít váš spustitelný soubor. Bylo jeho stahování dokončeno? Jsou oprávnění nastavena na spustitelný soubor?",
"portReminder": "Zjistili jsme, že server {} byl spuštěn poprvé. Ujistěte se, že jste přesměrovali port {} přes váš směrovač/firewall, aby byl tento port vzdáleně přístupný z internetu.",
"start-error": "Server {} se nepodařilo spustit s kódem chyby: {}",
"terribleFailure": "Jaké strašné selhání!",
"superError": "Pro dokončení této akce musíte být super uživatel.",
"fileError": "Typ souboru musí být obrázek."
},
"footer": {
"allRightsReserved": "Všechna práva vyhrazena",
"copyright": "Autorská práva",
"version": "Verze"
},
"login": {
"forgotPassword": "Zapomenuté heslo",
"login": "Přihlásit se",
"password": "Heslo",
"username": "Uživatelské jméno"
},
"notify": {
"activityLog": "Záznamy o činnosti",
"backupComplete": "Zálohování serveru {} bylo úspěšně dokončeno",
"backupStarted": "Bylo spuštěno zálohování serveru {}",
"downloadLogs": "Stáhnout protokoly podpory?",
"finishedPreparing": "Dokončili jsme přípravu protokolů podpory. Klikněte na tlačítko Stáhnout pro stažení",
"logout": "Odhlásit se",
"preparingLogs": " Počkejte prosím, než připravíme vaše protokoly... Až budou připraveny, pošleme vám oznámení. U rozsáhlých zavádění to může chvíli trvat.",
"supportLogs": "Protokoly podpory"
},
"panelConfig": {
"adminControls": "Ovládání správce",
"allowedServers": "Povolené servery",
"assignedRoles": "Přidělené role",
"cancel": "Zrušit",
"clearComms": "Vymazat nevykonané příkazy",
"delete": "Smazat",
"edit": "Upravit",
"enabled": "Zapnuto",
"match": "Hesla musí být stejná",
"newRole": "Přidat novou roli",
"newUser": "Přidat nového uživatele",
"pageTitle": "Nastavení panelu",
"role": "Role",
"roles": "Role",
"roleUsers": "Uživatelé s rolí",
"save": "Uložit",
"superConfirm": "Postupujte pouze v případě, že chcete, aby měl tento uživatel přístup ke VŠEM (ke všem uživatelským účtům, serverům, nastavení panelu atd.). Může vám dokonce odebrat práva superuživatele.",
"superConfirmTitle": "Povolit superuživatele? Jste si jisti?",
"user": "Uživatel",
"users": "Uživatelé",
"loginImage": "Nahrajte obrázek na pozadí přihlašovací obrazovky.",
"backgroundUpload": "Nahrání pozadí",
"loginBackground": "Přihlašovací obrázek na pozadí",
"select": "Vyberte",
"selectImage": "Vyberte obrázek",
"preview": "Náhled"
},
"rolesConfig": {
"config": "Nastavení role",
"configDesc": "Zde můžete změnit nastavení své role.",
"configUpdate": "Poslední aktualizace: ",
"created": "Vytvořený: ",
"delRole": "Smazat roli",
"doesNotExist": "Nemůžete odstranit něco, co ještě neexistuje",
"pageTitle": "Upravit roli",
"pageTitleNew": "Nová role",
"permAccess": "Přístup?",
"permName": "Název oprávnění",
"permsServer": "Oprávnění, která má tato role pro tyto servery",
"roleConfigArea": "Oblast nastavení role",
"roleDesc": "Jak byste chtěl aby se tato role jmenovala?",
"roleName": "Název role: ",
"rolePerms": "Oprávnění role",
"roleServers": "Povolené servery",
"roleTitle": "Nastavení rolí",
"roleUserName": "Uživatelské jméno",
"roleUsers": "Uživatelé role: ",
"serverAccess": "Přístup?",
"serverName": "Název serveru",
"serversDesc": "servery, ke kterým má tato role přístup",
"selectManager": "Výběr manažera pro tuto roli"
},
"serverBackups": {
"backupAtMidnight": "Automatické zálohování o půlnoci?",
"backupNow": "Zálohovat nyní!",
"backupTask": "Bylo spuštěno zálohování.",
"cancel": "Zrušit",
"clickExclude": "Kliknutím vyberete výjimku",
"compress": "Komprimovat zálohu",
"confirm": "Potvrdit",
"confirmDelete": "Chcete tuto zálohu odstranit? Tuto akci nelze vrátit zpět.",
"confirmRestore": "Jste si jisti, že chcete provést obnovu z této zálohy. Všechny aktuální soubory serveru se změní na stav zálohy a nebude možné je obnovit.",
"currentBackups": "Aktuální zálohy",
"delete": "Smazat",
"destroyBackup": "Zničit zálohu \" + file_to_del + \"?",
"download": "Stáhnout",
"excludedBackups": "Vyloučené cesty: ",
"excludedChoose": "Vyberte cesty, které chcete ze zálohování vyloučit.",
"exclusionsTitle": "Vyloučení ze zálohování",
"maxBackups": "Maximální počet záloh",
"maxBackupsDesc": "Crafty neuloží více než N záloh a odstraní nejstarší (zadejte 0 pro zachování všech).",
"options": "Nastavení",
"path": "Cesta",
"restore": "Obnovit",
"restoring": "Obnovení zálohy. To může chvíli trvat. Buďte prosím trpěliví.",
"save": "Uložit",
"shutdown": "Vypnout server po dobu zálohování",
"size": "Velikost",
"storageLocation": "Umístění úložiště",
"storageLocationDesc": "Kam chcete ukládat zálohy?"
},
"serverConfig": {
"bePatientDelete": "Buďte prosím trpěliví, než odstraníme váš server z panelu Crafty. Tato obrazovka se za chvíli zavře.",
"bePatientDeleteFiles": "Buďte prosím trpěliví, než odstraníme váš server z panelu Crafty a všechny jeho soubory. Tato obrazovka se za chvíli zavře.",
"bePatientUpdate": "Prosím, buďte trpěliví, dokud server neaktualizujeme. Doba stahování se může lišit v závislosti na rychlosti vašeho internetu.<br /> Tato obrazovka se za chvíli aktualizuje",
"cancel": "Zrušit",
"crashTime": "Časový limit havárie",
"crashTimeDesc": "Jak dlouho bychom měli čekat, než budeme váš server považovat za havarovaný?",
"deleteFilesQuestion": "Odstranit soubory serveru z přístroje?",
"deleteFilesQuestionMessage": "Chcete, aby Crafty odstranil všechny soubory serveru z hostitelského počítače? <br><br><strong>To se týká i zálohování serverů.</strong>",
"deleteServer": "Odstranit server",
"deleteServerQuestion": "Odstranit server?",
"deleteServerQuestionMessage": "Opravdu chcete tento server odstranit? Po tomto kroku již není cesty zpět...",
"exeUpdateURL": "Adresa URL pro aktualizaci spustitelných souborů serveru",
"exeUpdateURLDesc": "Adresa URL pro přímé stahování aktualizací.",
"javaNoChange": "Nepřepisujte",
"javaVersion": "Přepsat aktuální verzi Javy",
"javaVersionDesc": "Pokud se chystáte přepsat Javu, ujistěte se, že je aktuální cesta k Javě v příkazu 'execution command' zabalena do uvozovek (výchozí proměnná 'java' je vyloučena).",
"noDelete": "Ne, vrať se zpět",
"noDeleteFiles": "Ne, stačí jen vyjmout z panelu",
"removeOldLogsAfter": "Odstranit staré protokoly po",
"removeOldLogsAfterDesc": "Kolik dní musí být soubor protokolu starý, aby byl smazán (0 je vypnuto)",
"save": "Uložit",
"sendingDelete": "Odstraňování serveru",
"sendingRequest": "Odeslání žádosti...",
"serverAutoStart": "Automatické spuštění serveru",
"serverAutostartDelay": "Zpoždění automatického spuštění serveru",
"serverAutostartDelayDesc": "Zpoždění před automatickým spuštěním (je-li povoleno níže)",
"serverCrashDetection": "Detekce pádu serveru",
"serverExecutable": "Spustitelný soubor serveru",
"serverExecutableDesc": "Spustitelný soubor pro server",
"serverExecutionCommand": "Příkaz pro spuštění serveru",
"serverExecutionCommandDesc": "Co se spustí ve skrytém terminálu",
"serverIP": "Adresa serveru",
"serverIPDesc": "IP adresa, ke které by se měl Crafty připojit pro statistiky (pokud máte problémy, zkuste místo 127.0.0.1 zadat skutečnou IP adresu).",
"serverLogLocation": "Umístění protokolu serveru",
"serverLogLocationDesc": "Cesta k souboru protokolu",
"serverName": "Název serveru",
"serverNameDesc": "Jak chcete aby se tento server jmenoval",
"serverPath": "Pracovní adresář serveru",
"serverPathDesc": "Absolutní úplná cesta (bez spustitelného souboru)",
"serverPort": "Port serveru",
"serverPortDesc": "Port Crafty should connect to for stats",
"serverStopCommand": "Příkaz pro zastavení serveru",
"serverStopCommandDesc": "Příkaz k odeslání programu pro jeho zastavení",
"showStatus": "Zobrazit na veřejné stavové stránce",
"stopBeforeDeleting": "Před odstraněním serveru jej prosím zastavte",
"update": "Aktualizovat spustitelný soubor",
"yesDelete": "Ano, smazat",
"yesDeleteFiles": "Ano, smazat soubory",
"shutdownTimeout": "Časový limit pro vypnutí",
"timeoutExplain1": "Jak dlouho bude Crafty čekat na vypnutí serveru po provedení příkazu",
"timeoutExplain2": "než proces ukončí."
},
"serverConfigHelp": {
"desc": "Zde můžete změnit konfiguraci serveru.",
"perms": [
"Doporučujeme <code>NEMĚNIT</code> cesty serveru spravované Craftym.",
"Změna cest <code>MŮŽE</code> něco rozbít, zejména v operačních systémech typu Linux, kde jsou práva k souborům více uzamčena.",
"<br /><br/>",
"Pokud máte pocit, že musíte změnit místo, kde je server umístěn, můžete tak učinit, pokud dáte uživateli \"crafty\" oprávnění ke čtení / zápisu k cestě k serveru.",
"<br />",
"<br />",
"V systému Linux to nejlépe provedete následujícím příkazem:<br />",
"<code>",
" sudo chown crafty:crafty /cesta/k/vašemu/serveru -R<br />",
" sudo chmod 2775 /cesta/k/vašemu/serveru -R<br />",
"</code>"
],
"title": "Oblast nastavení serveru"
},
"serverDetails": {
"backup": "Záloha",
"config": "Nastavení",
"files": "Soubory",
"logs": "Protokoly",
"playerControls": "Správa hráčů",
"schedule": "Harmonogram",
"serverDetails": "Podrobnosti o serveru",
"terminal": "Terminál",
"metrics": "Metrika",
"reset": "Obnovit posuvník",
"filter": "Filtrovat protokoly",
"filterList": "Filtrovaná slova"
},
"serverFiles": {
"clickUpload": "Klikněte sem a vyberte své soubory",
"close": "Zavřít",
"createDir": "Vytvořit složku",
"createDirQuestion": "Jaký chcete pojmenovat novou složku?",
"createFile": "Vytvořit soubor",
"createFileQuestion": "Jaké jméno chcete zvolit pro nový soubor?",
"default": "Výchozí",
"delete": "Smazat",
"deleteItemQuestion": "Jste si jisti, že chcete odstranit \" + name + \"?",
"deleteItemQuestionMessage": "Odstraňujete \\\"\" + path + \"\\\"!<br/><br/>Tato akce bude nevratná a navždy ztracená!",
"download": "Stáhnout",
"editingFile": "Upravit soubor",
"error": "Chyba při získání souborů",
"fileReadError": "Chyba při čtení souboru",
"files": "Soubory",
"keybindings": "Klávesové zkratky",
"loadingRecords": "Načítání souborů...",
"noDelete": "Ne",
"noscript": "Správce souborů nefunguje bez JavaScriptu",
"rename": "Přejmenovat",
"renameItemQuestion": "Jaký by měl být nový název?",
"save": "Uložit",
"size": "Přepnout velikost editoru",
"stayHere": "NEOPOUŠTĚJTE TUTO STRÁNKU!",
"unsupportedLanguage": "Varování: Tento typ souboru není podporovaný",
"unzip": "Rozbalit",
"upload": "Nahrát",
"uploadTitle": "Nahrát soubory do: ",
"waitUpload": "Počkejte prosím, než se vaše soubory nahrají... To může chvíli trvat.",
"yesDelete": "Ano, chápu následky"
},
"serverPlayerManagement": {
"bannedPlayers": "Zabanovaní hráči",
"loadingBannedPlayers": "Načítání zabanovaných hráčů",
"players": "Hráči"
},
"serverScheduleConfig": {
"backup": "Zálohovat server",
"select": "Základní / Cron / Řetězová reakce",
"basic": "Základní",
"children": "Propojené dětské úlohy:",
"command": "Příkaz",
"command-explain": "Jaký příkaz máme provést? Neuvádějte znak '/'",
"cron": "Cron",
"cron-explain": "Zadejte řetězec cronu -- POZNÁMKA: 0 = Pondělí v poslední možnosti.",
"custom": "Vlastní příkaz",
"days": "Dny",
"enabled": "Zapnuto",
"hours": "Hodiny",
"interval": "Interval",
"interval-explain": "Jak často chcete, aby se tento plán prováděl?",
"minutes": "Minuty",
"offset": "Posunutí prodlevy",
"offset-explain": "Jak dlouho bychom měli čekat na spuštění této úlohy po spuštění první úlohy? (sekund)",
"one-time": "Odstranit po provedení",
"parent": "Výběr plánu rodiče",
"parent-explain": "Který plán by měl spustit tento?",
"reaction": "Reakce",
"restart": "Restartovat server",
"start": "Spustit server",
"stop": "Vypnout server",
"time": "Čas",
"time-explain": "V kolik hodin chcete, aby byl váš plán spuštěn?"
},
"serverSchedules": {
"scheduledTasks": "Naplánované úlohy",
"create": "Vytvořit nový plán",
"name": "Název",
"action": "Akce",
"command": "Příkaz",
"interval": "Interval",
"nextRun": "Příští spuštění",
"enabled": "Zapnuto",
"edit": "Upravit",
"every": "Každý",
"yes": "Ano",
"no": "Ne",
"cron": "Řetězec Crong",
"details": "Podrobnosti o plánu",
"child": "Dítě plánu s ID",
"areYouSure": "Odstranění naplánované úlohy?",
"close": "Zavřít",
"delete": "Odstranit",
"cancel": "Zrušit",
"cannotSee": "Nevidíte všechno?",
"cannotSeeOnMobile": "Kliknutím na naplánovanou úlohu získáte podrobné informace.",
"confirm": "Potvrdit",
"confirmDelete": "Chcete tuto naplánovanou úlohu odstranit? Tuto akci nelze vrátit zpět."
},
"serverStats": {
"cpuUsage": "Využití CPU",
"description": "Popis",
"errorCalculatingUptime": "Chyba při výpočtu doby provozu",
"memUsage": "Využití paměti",
"offline": "Offline",
"online": "Online",
"players": "Hráči",
"serverStarted": "Server spuštěn",
"serverStatus": "Stav serveru",
"serverTime": "UTC čas",
"serverTimeZone": "Časové pásmo serveru",
"serverUptime": "Doba provozu serveru",
"starting": "Zpožděný start",
"unableToConnect": "Nelze se připojit",
"version": "Verze"
},
"serverTerm": {
"commandInput": "Zadejte příkaz",
"delay-explained": "Služba/agent byla nedávno spuštěna a zpožďuje spuštění instance minecraft serveru.",
"importing": "Importování...",
"restart": "Restartovat",
"sendCommand": "Odeslat příkaz",
"start": "Spustit",
"starting": "Zpožděný start",
"stop": "Zastavit",
"stopScroll": "Zastavit automatické posouvání",
"updating": "Aktualizace...",
"installing": "Instalace..."
},
"serverMetrics": {
"resetZoom": "Reset Zoom",
"zoomHint1": "Chcete-li graf přiblížit, podržte klávesu Shift a poté použijte kolečko myši.",
"zoomHint2": "Případně můžete podržet klávesu Shift a kliknout a přetáhnout oblast, kterou chcete přiblížit."
},
"serverWizard": {
"absoluteServerPath": "Absolutní cesta k serveru",
"absoluteZipPath": "Absolutní cesta k serveru",
"addRole": "Přidání serveru k existujícím rolím",
"autoCreate": "Pokud nebude vybrána žádná, Crafty ji vyrobí!",
"bePatient": "Buďte prosím trpěliví, protože musíme ' + (importing ? 'importovat' : 'stáhnout') + ' server",
"buildServer": "Sestavit server!",
"clickRoot": "Klikněte zde pro výběr kořenového adresáře",
"close": "Zavřít",
"defaultPort": "25565 výchozí hodnota",
"downloading": "Stahování serveru...",
"explainRoot": "Klikněte na tlačítko níže a vyberte kořenový adresář vašeho serveru z archivu.",
"importing": "Importování serveru...",
"importServer": "Importování existujícího serveru",
"importServerButton": "Importovat server!",
"importZip": "Imporovat ze souboru Zip",
"uploadZip": "Nahrání souboru Zip pro importování serveru",
"maxMem": "Maximální paměť",
"minMem": "Minimální paměť",
"myNewServer": "Nový server",
"newServer": "Vytvořit nový server",
"quickSettings": "Rychlé nastavení",
"quickSettingsDescription": "Nebojte se, můžete je změnit později.",
"resetForm": "Obnovit nastavení formuláře",
"save": "Uložit",
"selectRole": "Vyberte roli(e)",
"selectRoot": "Vyberte kořenový adresář archivu",
"selectType": "Typ serveru (Vanilla, Servery, Modované atd.)",
"selectServer": "Vyberte server",
"selectVersion": "Vyberte verzi",
"selectZipDir": "Vyberte adresář v archivu, ze kterého mají být soubory rozbaleny.",
"serverJar": "Soubor spustitelný serverem",
"serverName": "Název serveru",
"serverPath": "Cesta k serveru",
"serverPort": "Port serveru",
"serverType": "Typ serveru",
"serverSelect": "Výběr serveru",
"serverVersion": "Verze serveru",
"sizeInGB": "Velikost v GB",
"zipPath": "Cesta k serveru"
},
"sidebar": {
"contribute": "Přispět",
"credits": "Zásluhy",
"dashboard": "Ovládací panel",
"documentation": "Dokumentace",
"navigation": "Navigace",
"newServer": "Vytvořit nový server",
"servers": "Servery"
},
"userConfig": {
"apiKey": "Klíče API",
"auth": "Autorizovaný? ",
"config": "Nastavení",
"configArea": "Uživatelská nastavení",
"configAreaDesc": "Zde můžete změnit všechna nastavení uživatele.",
"confirmDelete": "Jste si jisti, že chcete tohoto uživatele odstranit? Tato akce je nevratná.",
"craftyPermDesc": "Oprávnění tohoto uživatele ",
"craftyPerms": "Oprávnění: ",
"created": "Vytvořeno: ",
"deleteUser": "Smazat uživatele: ",
"deleteUserB": "Smazat uživatele",
"delSuper": "Super uživatele nelze odstranit",
"enabled": "Povolen",
"gravDesc": "Tento e-mail je určen výhradně pro použití se službou Gravatar™. Crafty nebude v žádném případě používat tento e-mail k ničemu jinému než k vyhledávání vašeho Gravataru™.",
"gravEmail": "Gravatar™ E-mail",
"lastIP": "Poslední IP: ",
"lastLogin": "Poslední přihlášení: ",
"lastUpdate": "Poslední aktualizace: ",
"leaveBlank": "Chcete-li upravit uživatele beze změny hesla, ponechte pole prázdné.",
"member": "Člen?",
"notExist": "Nemůžete odstranit něco, co neexistuje!",
"pageTitle": "Upravit uživatele",
"pageTitleNew": "Vytvořit uživatele",
"password": "Nové heslo",
"permName": "Název povolení",
"repeat": "Zopakujte heslo",
"roleName": "Název role",
"super": "Super uživatel",
"userLang": "Jazyk uživatele",
"userTheme": "Motiv UI",
"userName": "Uživatelské jméno",
"userNameDesc": "Jak chcete aby se tento uživatel jmenoval?",
"userRoles": "Role uživatele",
"userRolesDesc": "Role, jejichž je tento uživatel členem.",
"userSettings": "Nastavení uživatele",
"uses": "Počet povolených použití (-1==bez omezení)",
"manager": "Správce",
"selectManager": "Vyberte Správce pro uživatele"
}
}

View File

@ -340,7 +340,9 @@
"yesDeleteFiles": "Yes, delete files",
"shutdownTimeout": "Shutdown Timeout",
"timeoutExplain1": "How long Crafty will wait for your server to shutdown after executing the",
"timeoutExplain2": "command before it forces the process down."
"timeoutExplain2": "command before it forces the process down.",
"statsHint1": "The port your server is running on should go here. This is just how Crafty opens a connection to your server for stats.",
"statsHint2": "This does not change the port of your server. You must still change the port in your server config file."
},
"serverConfigHelp": {
"desc": "Here is where you can change the configuration of your server",

View File

@ -1,20 +1,27 @@
<?xml version="1.0"?>
<Container version="2">
<Beta>True</Beta>
<Beta>False</Beta>
<Name>Crafty-4</Name>
<Repository>registry.gitlab.com/crafty-controller/crafty-4:latest</Repository>
<Registry>registry.gitlab.com/crafty-controller/crafty-4</Registry>
<Repository>arcadiatechnology/crafty-4</Repository>
<Registry>https://hub.docker.com/r/arcadiatechnology/crafty-4</Registry>
<Network>bridge</Network>
<MyIP/>
<Shell>sh</Shell>
<Privileged>false</Privileged>
<Support>https://discord.gg/9VJPhCE</Support>
<Project>https://craftycontrol.com/</Project>
<Overview>Crafty 4 is the next iteration of our Minecraft Server Wrapper / Controller / Launcher. Boasting a clean new look, rebuilt from the ground up. Crafty 4 brings a whole host of new features such as Bedrock support. With SteamCMD support on the way!&#xD;
Default login Credentrails are username: "admin" password: "crafty"&#xD;
Crafty 4 is the successor of Crafty Controller, the Docker image is no longer maintained on DockerHub. (now on GitLab)&#xD;
For official support join the Discord: https://discord.gg/9VJPhCE&#xD;
For migration from 3.x please refer to the documentation: https://wiki.craftycontrol.com/en/4/&#xD;
<License>GNU GPL V3</License>
<Branch>
<Tag>latest</Tag>
<TagDescription>Latest version of Crafty, which should be used for production purposes as it is the most stable</TagDescription>
</Branch>
<Branch>
<Tag>dev</Tag>
<TagDescription>Development version of Crafty, only generally used for testing purposes, because of its less stable nature</TagDescription>
</Branch>
<Screenshot>https://wiki.craftycontrol.com/uploads/en/crafty%204%20dashboard%20with%20one%20server.jpeg</Screenshot>
<Screenshot>https://wiki.craftycontrol.com/uploads/en/crafty%204%20server%20setup%20details.png</Screenshot>
<Overview>Crafty 4 is the next iteration of our Minecraft Server Wrapper / Controller / Launcher. [br]Boasting a clean new look, rebuilt from the ground up. [br] [br] Crafty 4 brings a whole host of new features such as Bedrock support. [br] With SteamCMD support on the way![br] **Default login Credentrails are username: "admin" password: "crafty". ** [br]Crafty 4 is the successor of Crafty Controller. [br]For official support join the Discord server https://discord.gg/9VJPhCE [br] For migration from 3.x please refer to the documentation: https://wiki.craftycontrol.com/en/4/
</Overview>
<Category>GameServers: Other:</Category>
<WebUI>https://[IP]:[PORT:8443]/</WebUI>
@ -26,12 +33,6 @@ For migration from 3.x please refer to the documentation: https://wiki.craftycon
<DateInstalled/>
<DonateText>Help to support Crafty on Kofi</DonateText>
<DonateLink>https://ko-fi.com/arcadiatech</DonateLink>
<Description>Crafty 4 is the next iteration of our Minecraft Server Wrapper / Controller / Launcher. Boasting a clean new look, rebuilt from the ground up. Crafty 4 brings a whole host of new features such as Bedrock support. With SteamCMD support on the way!&#xD;
Default login Credentrails are username: "admin" password: "crafty"&#xD;
Crafty 4 is the successor of Crafty Controller, the Docker image is no longer maintained on DockerHub. (now on GitLab)&#xD;
For official support join the Discord: https://discord.gg/9VJPhCE&#xD;
For migration from 3.x please refer to the documentation: https://wiki.craftycontrol.com/en/4/&#xD;
</Description>
<Networking>
<Mode>bridge</Mode>
<Publish>