Merge branch 'dev' into refactor/server_ids

This commit is contained in:
Zedifus 2024-03-03 01:32:44 +00:00
commit e0bb2eaaa0
26 changed files with 281 additions and 139 deletions

View File

@ -0,0 +1,15 @@
#!/bin/bash
# Prompt the user for the directory path
read -p "Enter the directory path to set permissions (/var/opt/minecraft/crafty): " directory_path
# Check if the script is running within a Docker container
if [ -f "/.dockerenv" ]; then
echo "Script is running within a Docker container. Exiting with error."
exit 1 # Exit with an error code if running in Docker
else
echo "Script is not running within a Docker container. Executing permissions changes..."
# Run the commands to set permissions
sudo chmod 700 $(find "$directory_path" -type d)
sudo chmod 644 $(find "$directory_path" -type f)
fi

View File

@ -9,10 +9,15 @@ TBD
- Make sure default.json is read from correct location ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/714))
- Do not allow users at server limit to clone servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/718))
- Fix bug where you cannot get to config with unloaded server ([Commit](https://gitlab.com/crafty-controller/crafty-4/-/commit/9de08973b6bb2ddf91283c5c6b0e189ff34f7e24))
- Fix forge install v1.20, 1.20.1 and 1.20.2 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/710))
- Fix Sanitisation on Passwords ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/715))
- Fix `Upload Imports` on unix systems, that have a space in the root dir name ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/722))
- Fix Bedrock downloads, add `www` to download URL ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/723))
### Tweaks
- Bump pyOpenSSL & cryptography for CVE-2024-0727, CVE-2023-50782 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/716))
- Bump cryptography for CVE-2024-26130 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/724))
### Lang
TBD
- Update `de_DE, en_EN, es_ES, fr_FR, he_IL, lol_EN, lv_LV, nl_BE pl_PL, th_TH, tr_TR, uk_UA, zh_CN` translations for `4.3.0` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/715))
<br><br>
## --- [4.2.3] - 2023/02/02

View File

@ -52,7 +52,7 @@ class UsersController:
},
"password": {
"type": "string",
"minLength": 8,
"minLength": self.helper.minimum_password_length,
"examples": ["crafty"],
"title": "Password",
},

View File

@ -82,11 +82,11 @@ class MainPrompt(cmd.Cmd):
# get new password from user
new_pass = getpass.getpass(prompt=f"NEW password for: {username} > ")
# check to make sure it fits our requirements.
if len(new_pass) > 512:
Console.warning("Passwords must be greater than 6char long and under 512")
return False
if len(new_pass) < 6:
Console.warning("Passwords must be greater than 6char long and under 512")
if len(new_pass) < self.helper.minimum_password_length:
Console.warning(
"Passwords must be greater than"
f" {self.helper.minimum_password_length} char long"
)
return False
# grab repeated password input
new_pass_conf = getpass.getpass(prompt="Re-enter your password: > ")

View File

@ -81,6 +81,7 @@ class Helpers:
self.update_available = False
self.ignored_names = ["crafty_managed.txt", "db_stats"]
self.crafty_starting = False
self.minimum_password_length = 8
@staticmethod
def auto_installer_fix(ex):
@ -117,7 +118,7 @@ class Helpers:
Get latest bedrock executable url \n\n
returns url if successful, False if not
"""
url = "https://minecraft.net/en-us/download/server/bedrock/"
url = "https://www.minecraft.net/en-us/download/server/bedrock/"
headers = {
"Accept-Encoding": "identity",
"Accept-Language": "en",

View File

@ -18,12 +18,19 @@ class DatabaseBuilder:
logger.info("Fresh Install Detected - Creating Default Settings")
Console.info("Fresh Install Detected - Creating Default Settings")
default_data = self.helper.find_default_password()
if password not in default_data:
if "password" not in default_data:
Console.help(
"No default password found. Using password created "
"by Crafty. Find it in app/config/default-creds.txt"
)
username = default_data.get("username", "admin")
if self.helper.minimum_password_length > default_data.get("password", password):
Console.critical(
"Default password too short"
" using Crafty's created default."
" Find it in app/config/default-creds.txt"
)
else:
password = default_data.get("password", password)
self.users_helper.add_user(

View File

@ -696,6 +696,10 @@ class ServerInstance:
version_param = version[0][0].split(".")
version_major = int(version_param[0])
version_minor = int(version_param[1])
if len(version_param) > 2:
version_sub = int(version_param[2])
else:
version_sub = 0
# Checking which version we are with
if version_major <= 1 and version_minor < 17:
@ -729,8 +733,8 @@ class ServerInstance:
server_obj.execution_command = execution_command
Console.debug(SUCCESSMSG)
elif version_major <= 1 and version_minor < 20:
# NEW VERSION >= 1.17 and <= 1.20
elif version_major <= 1 and version_minor <= 20 and version_sub < 3:
# NEW VERSION >= 1.17 and <= 1.20.2
# (no jar file in server dir, only run.bat and run.sh)
run_file_path = ""
@ -777,7 +781,7 @@ class ServerInstance:
server_obj.execution_command = execution_command
Console.debug(SUCCESSMSG)
else:
# NEW VERSION >= 1.20
# NEW VERSION >= 1.20.3
# (executable jar is back in server dir)
# Retrieving the executable jar filename

View File

@ -1,5 +1,8 @@
import logging
import json
import nh3
from jsonschema import validate
from jsonschema.exceptions import ValidationError
from app.classes.shared.helpers import Helpers
from app.classes.models.users import HelperUsers
@ -45,7 +48,7 @@ class PublicHandler(BaseHandler):
}
if self.request.query:
page_data["query"] = self.request.query
page_data["query"] = self.request.query_arguments.get("next")[0].decode()
# sensible defaults
template = "public/404.html"
@ -75,11 +78,7 @@ class PublicHandler(BaseHandler):
# if we have no page, let's go to login
else:
if self.request.query:
self.redirect("/login?" + self.request.query)
else:
self.redirect("/login")
return
return self.redirect("/login")
self.render(
template,
@ -89,33 +88,61 @@ class PublicHandler(BaseHandler):
)
def post(self, page=None):
# pylint: disable=no-member
error = nh3.clean(self.get_argument("error", "Invalid Login!"))
error_msg = nh3.clean(self.get_argument("error_msg", ""))
# pylint: enable=no-member
login_schema = {
"type": "object",
"properties": {
"username": {
"type": "string",
},
"password": {"type": "string"},
},
"required": ["username", "password"],
"additionalProperties": False,
}
try:
data = json.loads(self.request.body)
except json.decoder.JSONDecodeError as e:
logger.error(
"Invalid JSON schema for API"
f" login attempt from {self.get_remote_ip()}"
)
return self.finish_json(
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
)
try:
validate(data, login_schema)
except ValidationError as e:
logger.error(
"Invalid JSON schema for API"
f" login attempt from {self.get_remote_ip()}"
)
return self.finish_json(
400,
{
"status": "error",
"error": "VWggb2ghIFN0aW5reS 🪠",
"error_data": str(e),
},
)
page_data = {
"version": self.helper.get_version_string(),
"error": error,
"lang": self.helper.get_setting("language"),
"lang_page": self.helper.get_lang_page(self.helper.get_setting("language")),
"query": "",
}
if self.request.query:
page_data["query"] = self.request.query
page_data["query"] = self.request.query_arguments.get("next")[0].decode()
if page == "login":
data = json.loads(self.request.body)
auth_log.info(
f"User attempting to authenticate from {self.get_remote_ip()}"
)
next_page = "/login"
if self.request.query:
next_page = "/login?" + self.request.query
# pylint: disable=no-member
entered_username = nh3.clean(self.get_argument("username"))
entered_password = self.get_argument("password")
# pylint: enable=no-member
entered_username = nh3.clean(data["username"]) # pylint: disable=no-member
entered_password = data["password"]
try:
user_id = HelperUsers.get_user_id_by_name(entered_username.lower())
@ -127,16 +154,18 @@ class PublicHandler(BaseHandler):
f" Authentication failed from remote IP {self.get_remote_ip()}"
" Users does not exist."
)
error_msg = "Incorrect username or password. Please try again."
self.finish_json(
403,
{
"status": "error",
"error": self.helper.translation.translate(
"login", "incorrect", self.helper.get_setting("language")
),
},
)
# self.clear_cookie("user")
# self.clear_cookie("user_data")
self.clear_cookie("token")
if self.request.query:
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}")
else:
self.redirect(f"/login?error_msg={error_msg}")
return
return self.clear_cookie("token")
# if we don't have a user
if not user_data:
auth_log.error(
@ -145,15 +174,18 @@ class PublicHandler(BaseHandler):
" User does not exist."
)
self.controller.log_attempt(self.get_remote_ip(), entered_username)
error_msg = "Incorrect username or password. Please try again."
self.finish_json(
403,
{
"status": "error",
"error": self.helper.translation.translate(
"login", "incorrect", self.helper.get_setting("language")
),
},
)
# self.clear_cookie("user")
# self.clear_cookie("user_data")
self.clear_cookie("token")
if self.request.query:
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}")
else:
self.redirect(f"/login?error_msg={error_msg}")
return
return self.clear_cookie("token")
# if they are disabled
if not user_data.enabled:
@ -163,19 +195,18 @@ class PublicHandler(BaseHandler):
" User account disabled"
)
self.controller.log_attempt(self.get_remote_ip(), entered_username)
error_msg = (
"User account disabled. Please contact "
"your system administrator for more info."
self.finish_json(
403,
{
"status": "error",
"error": self.helper.translation.translate(
"login", "disabled", self.helper.get_setting("language")
),
},
)
# self.clear_cookie("user")
# self.clear_cookie("user_data")
self.clear_cookie("token")
if self.request.query:
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}")
else:
self.redirect(f"/login?error_msg={error_msg}")
return
return self.clear_cookie("token")
login_result = self.helper.verify_pass(entered_password, user_data.password)
# Valid Login
@ -200,13 +231,11 @@ class PublicHandler(BaseHandler):
user_data.user_id, "Logged in", 0, self.get_remote_ip()
)
if self.request.query_arguments.get("next"):
next_page = self.request.query_arguments.get("next")[0].decode()
else:
next_page = "/panel/dashboard"
return self.finish_json(
200, {"status": "ok", "data": {"message": "login successful!"}}
)
self.redirect(next_page)
else:
# We'll continue on and handle unsuccessful logins
auth_log.error(
f"User attempted to log into {entered_username}."
f" Authentication failed from remote IP {self.get_remote_ip()}"
@ -215,17 +244,21 @@ class PublicHandler(BaseHandler):
# self.clear_cookie("user")
# self.clear_cookie("user_data")
self.clear_cookie("token")
error_msg = "Incorrect username or password. Please try again."
error_msg = self.helper.translation.translate(
"login", "incorrect", self.helper.get_setting("language")
)
if entered_password == "app/config/default-creds.txt":
error_msg += ". "
error_msg += self.helper.translation.translate(
"login", "defaultPath", self.helper.get_setting("language")
)
# log this failed login attempt
self.controller.management.add_to_audit_log(
user_data.user_id, "Tried to log in", 0, self.get_remote_ip()
)
if self.request.query:
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}")
return self.finish_json(
403,
{"status": "error", "error": error_msg},
)
else:
self.redirect(f"/login?error_msg={error_msg}")
else:
if self.request.query:
self.redirect("/login?" + self.request.query)
else:
self.redirect("/login")
self.redirect("/login?")

View File

@ -17,7 +17,7 @@ login_schema = {
"minLength": 4,
"pattern": "^[a-z0-9_]+$",
},
"password": {"type": "string", "maxLength": 20, "minLength": 4},
"password": {"type": "string", "minLength": 4},
},
"required": ["username", "password"],
"additionalProperties": False,

View File

@ -77,11 +77,7 @@
box-shadow: 0 12px 16px 0 hsla(0, 0%, 0%, 0.4);
}
</style>
{% if data['query'] %}
<form action="/login?{{ data['query'] }}" method="post">
{% else %}
<form action="/login" method="post">
{% end %}
<form id="login-form" data-query="{{ data.get('query', None) }}">
{% raw xsrf_form_html() %}
<div class="form-group">
<label class="label">{{ translate('login', 'username', data['lang']) }}</label>
@ -103,16 +99,14 @@
<button class="login-input btn btn-primary submit-btn btn-block">{{ translate('login', 'login',
data['lang']) }}</button>
</div>
{% if error_msg is not None %}
<fieldset style="color: red; text-align: center;">
<span>{{error_msg}}</span>
<fieldset id="error-field" style="color: red; text-align: center;">
</fieldset>
{% end %}
<div class="form-group d-flex justify-content-between">
<div class="form-check form-check-flat mt-0">
&nbsp;
</div>
<button onclick="resetPass()" id="#resetPass" form="" class="btn btn-outline-primary btn-sm forgot-password ">{{ translate('login', 'forgotPassword',
<button onclick="resetPass()" id="#resetPass" form=""
class="btn btn-outline-primary btn-sm forgot-password ">{{ translate('login', 'forgotPassword',
data['lang']) }}</button>
</div>
@ -155,13 +149,13 @@
document.getElementById('login-form-background').style.background = 'rgb(34, 36, 55, ' + (opacity / 100) + ')';
//Register Service worker for mobile app
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/static/assets/js/shared/service-worker.js', {scope: '/'})
navigator.serviceWorker.register('/static/assets/js/shared/service-worker.js', { scope: '/' })
.then(function (registration) {
console.log('Service Worker Registered');
});
}
});
async function resetPass(){
async function resetPass() {
let res = await fetch(`/api/v2/crafty/resetPass/`, {
method: 'GET',
});
@ -170,7 +164,38 @@
bootbox.alert(responseData.data)
}
$("#login-form").on("submit", async function (e) {
e.preventDefault();
let loginForm = document.getElementById("login-form");
let formData = new FormData(loginForm);
//Create an object from the form data entries
let formDataObject = Object.fromEntries(formData.entries());
console.log(formDataObject)
let res = await fetch(`/login`, {
method: 'POST',
headers: {
'X-XSRFToken': formDataObject._xsrf,
"Content-Type": "application/json"
},
body: JSON.stringify({
"username": formDataObject.username,
"password": formDataObject.password
}),
});
let responseData = await res.json();
if (responseData.status === "ok") {
console.log("OK")
if ($("#login-form").data("query")) {
location.href = `${$("#login-form").data("query")}`;
} else {
location.href = `/panel/dashboard`
}
} else {
$("#error-field").html(responseData.error);
}
});
</script>
<style>
.modal-content {

View File

@ -556,7 +556,7 @@
xmlHttpRequest.addEventListener('load', (event) => {
if (event.target.responseText == 'success') {
console.log('Upload for file', file.name, 'was successful!')
document.getElementById("upload_input").innerHTML = `<div class="card-header header-sm d-flex justify-content-between align-items-center" style="width: 100%;"><input value=${fileName} type="text" id="file-uploaded" disabled></input> 🔒</div>`;
$("#upload_input").html(`<div class="card-header header-sm d-flex justify-content-between align-items-center" style="width: 100%;"><input value="${decodeURIComponent(fileName)}" type="text" id="file-uploaded" disabled></input> 🔒</div>`);
document.getElementById("lower_half").style.visibility = "visible";
}
else {

View File

@ -881,7 +881,7 @@
xmlHttpRequest.addEventListener('load', (event) => {
if (event.target.responseText == 'success') {
console.log('Upload for file', file.name, 'was successful!')
document.getElementById("upload_input").innerHTML = `<div class="card-header header-sm d-flex justify-content-between align-items-center" style="width: 100%;"><input value=${fileName} type="text" id="file-uploaded" disabled></input> 🔒</div>`;
$("#upload_input").html(`<div class="card-header header-sm d-flex justify-content-between align-items-center" style="width: 100%;"><input value="${fileName}" type="text" id="file-uploaded" disabled></input> 🔒</div>`);
document.getElementById("lower_half").style.visibility = "visible";
document.getElementById("lower_half").hidden = false;
}

View File

@ -215,7 +215,10 @@
"version": "Version"
},
"login": {
"defaultPath": "Der eingegebene Text ist der Pfad zum Passwort, nicht das Passwort selbst. Das Standartpasswort kann unter diesen Pfad eingesehen werden.",
"disabled": "Account gesperrt. Für weitere Informationen den Serveradministrator kontaktieren",
"forgotPassword": "Passwort vergessen",
"incorrect": "Benutzername oder Passwort falsch",
"login": "Einloggen",
"password": "Passwort",
"username": "Nutzername",

View File

@ -215,7 +215,10 @@
"version": "Version"
},
"login": {
"defaultPath": "The password you entered is the default credential path, not the password. Please find the default password in that location.",
"disabled": "User account disabled. Please contact your system administrator for more info.",
"forgotPassword": "Forgot Password",
"incorrect": "Incorrect username or password",
"login": "Log In",
"password": "Password",
"username": "Username",

View File

@ -111,6 +111,7 @@
"starting": "Inicio-retrasado",
"status": "Estado",
"stop": "Detener",
"storage": "Almacenamiento",
"version": "Versión",
"welcome": "Bienvenido a Crafty Controller"
},
@ -214,7 +215,10 @@
"version": "Versión"
},
"login": {
"defaultPath": "La contraseña introducida es la ruta default de las credenciales, no la contraseña. Busca la contraseña accediendo a la carpeta de la ruta",
"disabled": "Cuenta del usuario desactivada. Porfavor contacta al administrador para mas informacion.",
"forgotPassword": "Olvidé mi contraseña",
"incorrect": "El nombre de usuario o contraseña es incorrecto",
"login": "Iniciar Sesión",
"password": "Contraseña",
"username": "Usuario",
@ -326,6 +330,7 @@
"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",
"countPlayers": "Incluir el servidor en la cuenta total de jugadores",
"crashTime": "Tiempo de espera por crasheo",
"crashTimeDesc": "¿Cuanto tiempo esperar para considerar el servidor como crasheado?",
"deleteFilesQuestion": "¿Eliminar archivos del servidor del host?",
@ -510,6 +515,7 @@
"cpuUsage": "Uso de CPU",
"description": "Descripción",
"errorCalculatingUptime": "Error calculando tiempo de actividad",
"loadingMotd": "Cargando MOTD",
"memUsage": "Uso de memoria",
"offline": "Desconectado",
"online": "En línea",
@ -577,6 +583,7 @@
"serverUpload": "Subir servidor comprimido",
"serverVersion": "Versión del servidor",
"sizeInGB": "Tamaño en GB",
"unsupported": "Versiones de Minecraft inferiores a la 1.8 no estan soportadas por Crafty. Es posible instalarlas. Resultados pueden variar.",
"uploadButton": "Subir",
"uploadZip": "Subir archivo Zip para importar servidor",
"zipPath": "Ruta del servidor"
@ -591,6 +598,15 @@
"newServer": "Crear nuevo Servidor",
"servers": "Servidores"
},
"startup": {
"almost": "Terminando. Espera un momento...",
"internals": "Configurando e inicializando los componentes internos de Crafty",
"internet": "Verificando conexion a internet",
"server": "Inicializando ",
"serverInit": "Inicializando Servidores",
"starting": "Crafty esta iniciando...",
"tasks": "Iniciando el programador de tareas"
},
"userConfig": {
"apiKey": "Claves API",
"auth": "¿Autorizado? ",

View File

@ -215,7 +215,10 @@
"version": "Version"
},
"login": {
"defaultPath": "Ce que tu as renseigné n'est pas le mot de passe, mais le chemin du fichier où le trouver.",
"disabled": "Ce compte est désactivé. Merci de contacter l'administrateur de ton serveur pour plus d'informations.",
"forgotPassword": "Mot de Passe Oublié",
"incorrect": "Identifiant et/ou mot de passe incorrect.",
"login": "Connexion",
"password": "Mot de Passe",
"username": "Nom d'Utilisateur",

View File

@ -215,7 +215,10 @@
"version": "גרסה"
},
"login": {
"defaultPath": "הסיסמה שהזנת היא נתיב האישורים המוגדר כברירת מחדל, ולא הסיסמה עצמה. אנא מצא את הסיסמה המוגדרת כברירת מחדל במיקום זה.",
"disabled": "חשבון המשתמש מושבת. אנא פנה למנהל המערכת שלך לקבלת מידע נוסף.",
"forgotPassword": "שכחתי סיסמה",
"incorrect": "שם משתמש או סיסמה שגויים",
"login": "התחברות",
"password": "סיסמה",
"username": "שם משתמש",

View File

@ -215,7 +215,10 @@
"version": "VERSHUN"
},
"login": {
"defaultPath": "Silleh hooman, dat iz da dafault secret path, not da passwurd. Plz find da default passwurd in dat spot.",
"disabled": "User account no play. Plz boop ur system hooman for moar infoz.",
"forgotPassword": "FORGWOTS YOUR SEEKRET",
"incorrect": "U gotz wrong name or passwurd",
"login": "WOG INZ",
"password": "SEEKRET",
"username": "USERNAEM",

View File

@ -216,7 +216,10 @@
"version": "Versija"
},
"login": {
"defaultPath": "Parole ko ievadijāt ir celš uz noklusētās paroles vietu, nevis noklusētā parole. Lūdzu apskatiet noklusēto paroli šajā vietā.",
"disabled": "Lietotāja konts atspējots. Lūdzu sazinieties ar savu sistēmas administratoru priekš papildus informācijas.",
"forgotPassword": "Aizmirsu Paroli",
"incorrect": "Nepareizs lietotājvārds vai parole",
"login": "Ieiet",
"password": "Parole",
"username": "Lietotājvārds",

View File

@ -215,7 +215,10 @@
"version": "Versie"
},
"login": {
"defaultPath": "Het ingevoerde wachtwoord is het pad naar de standaardreferentie, niet het wachtwoord zelf. Raadpleeg de standaardwachtwoord op de aangegeven locatie.",
"disabled": "Gebruikersaccount uitgeschakeld. Neem voor meer informatie contact op met uw systeembeheerder.",
"forgotPassword": "Wachtwoord vergeten",
"incorrect": "Verkeerde gebruikersnaam of wachtwoord",
"login": "Log In",
"password": "Wachtwoord",
"username": "gebruikersnaam",

View File

@ -215,7 +215,10 @@
"version": "Wersja"
},
"login": {
"defaultPath": "Hasło które wprowadziłeś jest podstawową ścieżką w której przechowywane są dane logowania. Znajdź podstawowe hasło w tej lokalizacji.",
"disabled": "Konto tego użytkownika jest wyłączone. Skontaktuj się z administratorem by uzyskać więcej informacji.",
"forgotPassword": "Zapomniałem hasła",
"incorrect": "Niepoprawny login lub hasło/Niepoprawna nazwa użytkownika lub hasło",
"login": "Zaloguj się",
"password": "Hasło",
"username": "Nazwa użytkownika",

View File

@ -215,7 +215,10 @@
"version": "เวอร์ชั่น"
},
"login": {
"defaultPath": "รหัสผ่านที่คุณกรอกคือเส้นทางข้อมูลเริ่มต้น ไม่ใช่รหัสผ่าน กรุณาค้นหารหัสผ่านเริ่มต้นในตำแหน่งนั้น",
"disabled": "บัญชีผู้ใช้ถูกปิดใช้งาน กรุณาติดต่อผู้ดูแลระบบของคุณสำหรับข้อมูลเพิ่มเติม",
"forgotPassword": "ลืมรหัสผ่าน",
"incorrect": "ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง",
"login": "เข้าสู่ระบบ",
"password": "รหัสผ่าน",
"username": "ชื่อผู้ใช้",

View File

@ -215,7 +215,10 @@
"version": "Sürüm"
},
"login": {
"defaultPath": "Girdiğiniz şifre varsayılan şifrenin konumudur, varsayılan şifre değil. Lütfen o konumda bulunan varsayılan şifreyi bulunuz.",
"disabled": "Bu kullanıcı hesabı engellenmiştir. Daha fazla bilgi için lütfen sunucu yöneticiniz ile konuşunuz.",
"forgotPassword": "Şifremi Unuttum",
"incorrect": "Kullanıcı adınız veya şifreniz yanlış.",
"login": "Oturum Aç",
"password": "Şifre",
"username": "Kullanıcı Adı",

View File

@ -85,7 +85,7 @@
"cpuCurFreq": "Швидкість CPU",
"cpuMaxFreq": "Максимальна швидкість CPU",
"cpuUsage": "Використання CPU",
"crashed": "Аварійне завершення",
"crashed": "Краш",
"dashboard": "Панель",
"delay-explained": "Служба/агент нещодавно запущено та затримує запуск серверів minecraft",
"host": "Хост",
@ -215,7 +215,10 @@
"version": "Версія"
},
"login": {
"defaultPath": "Пароль, який ви ввели, є шляхом до облікових даних за умовчанням, а не паролем. Будь ласка, знайдіть стандартний пароль у цьому місці.",
"disabled": "Користувача вимкнено. Зверніться до вашого системного адміністратора за допомогою.",
"forgotPassword": "Забули пароль",
"incorrect": "Неправильний логін або пароль",
"login": "Вхід",
"password": "Пароль",
"username": "Логін",
@ -351,7 +354,7 @@
"sendingRequest": "Надсилання вашого запиту...",
"serverAutoStart": "Сервер Авто-старт",
"serverAutostartDelay": "Сервер Авто-старт затримка",
"serverAutostartDelayDesc": "Затримка Авто-старту сервера (Якщо увімкнуто раніше)",
"serverAutostartDelayDesc": "Затримка Авто-старту сервера (Після запуску Crafty))",
"serverCrashDetection": "Детектор крашу сервера",
"serverExecutable": "Виконуваний файл Серверу",
"serverExecutableDesc": "Це виконуваний файл для запуску сервера",
@ -369,7 +372,7 @@
"serverPortDesc": "Цей порт призначений для статистики Crafty",
"serverStopCommand": "Команда зупинки сервера",
"serverStopCommandDesc": "Команда яка буде надсилатись, щоб зупинити сервер",
"showStatus": "Показувати на публічній сторінці статус",
"showStatus": "Показувати статус на публічній сторінці",
"shutdownTimeout": "Час відклику зупинки",
"statsHint1": "Цей порт на якому працює сервер. Це потрібно лиш для того щоб Crafty міг виводити статистику для цього сервера.",
"statsHint2": "Це не змінює порт вашого сервера. Ви мусите власноруч змінити налаштування в server.properties або іншому конфігураційному файлі.",
@ -406,7 +409,7 @@
"logs": "Логи",
"metrics": "Графік",
"playerControls": "Керування Гравцями",
"reset": "Повернутись нагору",
"reset": "Вниз",
"schedule": "Розклад",
"serverDetails": "Деталі сервера",
"terminal": "Термінал"

View File

@ -215,7 +215,10 @@
"version": "版本"
},
"login": {
"defaultPath": "您输入的密码是默认凭据的路径,不是其中的密码。请在此路径中找到默认密码。",
"disabled": "用户账号已禁用。请联系您的系统管理员以了解更多信息。",
"forgotPassword": "忘记密码",
"incorrect": "用户名或密码错误",
"login": "登录",
"password": "密码",
"username": "用户名",

View File

@ -4,7 +4,7 @@ argon2-cffi==23.1.0
cached_property==1.5.2
colorama==0.4.6
croniter==1.4.1
cryptography==42.0.2
cryptography==42.0.4
libgravatar==1.0.4
nh3==0.2.14
packaging==23.2