mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'dev' into dev-StatsDBImprovement
This commit is contained in:
commit
99b28efd33
@ -5,6 +5,7 @@
|
||||
---
|
||||
stages:
|
||||
- lint
|
||||
- test
|
||||
- prod-deployment
|
||||
- dev-deployment
|
||||
|
||||
@ -16,7 +17,7 @@ yamllint:
|
||||
stage: lint
|
||||
image: registry.gitlab.com/pipeline-components/yamllint:latest
|
||||
tags:
|
||||
- "docker"
|
||||
- docker
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
- if: "$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS"
|
||||
@ -28,7 +29,7 @@ jsonlint:
|
||||
stage: lint
|
||||
image: registry.gitlab.com/pipeline-components/jsonlint:latest
|
||||
tags:
|
||||
- "docker"
|
||||
- docker
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
- if: "$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS"
|
||||
@ -42,7 +43,7 @@ black:
|
||||
stage: lint
|
||||
image: registry.gitlab.com/pipeline-components/black:latest
|
||||
tags:
|
||||
- "docker"
|
||||
- docker
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
- if: "$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS"
|
||||
@ -54,7 +55,7 @@ pylint:
|
||||
stage: lint
|
||||
image: registry.gitlab.com/pipeline-components/pylint:latest
|
||||
tags:
|
||||
- "docker"
|
||||
- docker
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
- if: "$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS"
|
||||
@ -84,7 +85,7 @@ docker-build-dev:
|
||||
- name: docker:dind
|
||||
stage: dev-deployment
|
||||
tags:
|
||||
- "docker_priv"
|
||||
- docker_priv
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'dev'
|
||||
environment:
|
||||
@ -139,7 +140,7 @@ docker-build-prod:
|
||||
- name: docker:dind
|
||||
stage: prod-deployment
|
||||
tags:
|
||||
- "docker_priv"
|
||||
- docker_priv
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
environment:
|
||||
@ -269,3 +270,31 @@ win-prod-build:
|
||||
- .\crafty_commander.exe
|
||||
exclude:
|
||||
- app\classes\**\*
|
||||
|
||||
sast:
|
||||
variables:
|
||||
SAST_EXCLUDED_PATHS: spec, test, tests, tmp, migrations, vendors
|
||||
SAST_BANDIT_EXCLUDED_PATHS: "'*/migrations/*, */vendors/*'"
|
||||
SAST_EXCLUDED_ANALYZERS: semgrep
|
||||
stage: test
|
||||
tags:
|
||||
- docker
|
||||
|
||||
secret_detection:
|
||||
variables:
|
||||
SECRET_DETECTION_EXCLUDED_PATHS: migrations, vendors
|
||||
tags:
|
||||
- docker
|
||||
|
||||
gemnasium-dependency_scanning:
|
||||
tags:
|
||||
- docker
|
||||
|
||||
gemnasium-python-dependency_scanning:
|
||||
tags:
|
||||
- docker
|
||||
|
||||
include:
|
||||
- template: Security/Dependency-Scanning.gitlab-ci.yml
|
||||
- template: Security/SAST.gitlab-ci.yml
|
||||
- template: Security/Secret-Detection.gitlab-ci.yml
|
||||
|
@ -443,7 +443,7 @@ ignored-classes=optparse.Values,thread._local,_thread._local
|
||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||
# and thus existing member attributes cannot be deduced by static analysis). It
|
||||
# supports qualified module names, as well as Unix pattern matching.
|
||||
ignored-modules=
|
||||
ignored-modules=jsonschema,orjson
|
||||
|
||||
# Show a hint with possible names when a member name was not found. The aspect
|
||||
# of finding the hint is based on edit distance.
|
||||
|
@ -328,7 +328,7 @@ class ServersController(metaclass=Singleton):
|
||||
server_data.append(
|
||||
{
|
||||
"server_data": server,
|
||||
"stats": DatabaseShortcuts.return_rows(latest)[0],
|
||||
"stats": latest,
|
||||
"user_command_permission": user_command_permission,
|
||||
}
|
||||
)
|
||||
|
@ -11,6 +11,11 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UsersController:
|
||||
class ApiPermissionDict(t.TypedDict):
|
||||
name: str
|
||||
quantity: int
|
||||
enabled: bool
|
||||
|
||||
def __init__(self, helper, users_helper, authentication):
|
||||
self.helper = helper
|
||||
self.users_helper = users_helper
|
||||
|
@ -1,4 +1,5 @@
|
||||
import logging
|
||||
import typing as t
|
||||
from enum import Enum
|
||||
from peewee import (
|
||||
ForeignKeyField,
|
||||
@ -99,7 +100,7 @@ class PermissionsCrafty:
|
||||
try:
|
||||
user_crafty = UserCrafty.get(UserCrafty.user_id == user_id)
|
||||
except DoesNotExist:
|
||||
user_crafty = UserCrafty.insert(
|
||||
UserCrafty.insert(
|
||||
{
|
||||
UserCrafty.user_id: user_id,
|
||||
UserCrafty.permissions: "000",
|
||||
@ -114,6 +115,13 @@ class PermissionsCrafty:
|
||||
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
|
||||
return user_crafty
|
||||
|
||||
@staticmethod
|
||||
def get_user_crafty_optional(user_id) -> t.Optional[UserCrafty]:
|
||||
try:
|
||||
return UserCrafty.get(UserCrafty.user_id == user_id)
|
||||
except DoesNotExist:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def add_user_crafty(user_id, uc_permissions):
|
||||
user_crafty = UserCrafty.insert(
|
||||
|
@ -66,10 +66,9 @@ class HelperRoles:
|
||||
@staticmethod
|
||||
def get_role_column(role_id: t.Union[str, int], column_name: str) -> t.Any:
|
||||
column = getattr(Roles, column_name)
|
||||
return model_to_dict(
|
||||
Roles.select(column).where(Roles.role_id == role_id).get(),
|
||||
only=[column],
|
||||
)[column_name]
|
||||
return getattr(
|
||||
Roles.select(column).where(Roles.role_id == role_id).get(), column_name
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def add_role(role_name):
|
||||
|
@ -205,7 +205,7 @@ class PermissionsServers:
|
||||
@staticmethod
|
||||
def get_user_permissions_mask(user: Users, server_id: str):
|
||||
if user.superuser:
|
||||
permissions_mask = "1" * len(PermissionsServers.get_permissions_list())
|
||||
permissions_mask = "1" * len(EnumPermissionsServer)
|
||||
else:
|
||||
roles_list = HelperUsers.get_user_roles_id(user.user_id)
|
||||
role_server = (
|
||||
@ -217,7 +217,7 @@ class PermissionsServers:
|
||||
try:
|
||||
permissions_mask = role_server[0].permissions
|
||||
except IndexError:
|
||||
permissions_mask = "0" * len(PermissionsServers.get_permissions_list())
|
||||
permissions_mask = "0" * len(EnumPermissionsServer)
|
||||
return permissions_mask
|
||||
|
||||
@staticmethod
|
||||
|
@ -1,6 +1,9 @@
|
||||
import os
|
||||
import logging
|
||||
import datetime
|
||||
import typing as t
|
||||
|
||||
from playhouse.shortcuts import model_to_dict
|
||||
|
||||
import typing as t
|
||||
|
||||
@ -10,14 +13,12 @@ from playhouse.shortcuts import model_to_dict
|
||||
|
||||
from app.classes.models.servers import Servers, HelperServers
|
||||
from app.classes.shared.helpers import Helpers
|
||||
from app.classes.shared.main_models import DatabaseShortcuts
|
||||
from app.classes.shared.migration import MigrationManager
|
||||
|
||||
try:
|
||||
from peewee import (
|
||||
SqliteDatabase,
|
||||
Model,
|
||||
DatabaseProxy,
|
||||
ForeignKeyField,
|
||||
CharField,
|
||||
AutoField,
|
||||
@ -25,6 +26,7 @@ try:
|
||||
BooleanField,
|
||||
IntegerField,
|
||||
FloatField,
|
||||
DoesNotExist,
|
||||
)
|
||||
|
||||
except ModuleNotFoundError as e:
|
||||
@ -33,8 +35,6 @@ except ModuleNotFoundError as e:
|
||||
logger = logging.getLogger(__name__)
|
||||
peewee_logger = logging.getLogger("peewee")
|
||||
peewee_logger.setLevel(logging.INFO)
|
||||
# database_stats_proxy = DatabaseProxy()
|
||||
|
||||
|
||||
# **********************************************************************************
|
||||
# Servers Stats Class
|
||||
@ -65,7 +65,6 @@ class ServerStats(Model):
|
||||
|
||||
class Meta:
|
||||
table_name = "server_stats"
|
||||
# database = database_stats_proxy
|
||||
|
||||
|
||||
# **********************************************************************************
|
||||
@ -102,14 +101,13 @@ class HelperServerStats:
|
||||
f"{helper_stats.migration_dir}", "stats"
|
||||
)
|
||||
helper_stats.db_path = db_file
|
||||
# database_stats_proxy.initialize(self.database)
|
||||
migration_manager = MigrationManager(self.database, helper_stats)
|
||||
migration_manager.up() # Automatically runs migrations
|
||||
# database_stats_proxy.initialize(self.database)
|
||||
except Exception as ex:
|
||||
logger.warning(
|
||||
f"Error try to look for the db_stats files for server : {ex}"
|
||||
)
|
||||
return None
|
||||
|
||||
def select_database(self):
|
||||
try:
|
||||
@ -122,29 +120,28 @@ class HelperServerStats:
|
||||
self.database = SqliteDatabase(
|
||||
db_file, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
|
||||
)
|
||||
# database_stats_proxy.initialize(self.database)
|
||||
except Exception as ex:
|
||||
logger.warning(
|
||||
f"Error try to look for the db_stats files for server : {ex}"
|
||||
)
|
||||
return None
|
||||
|
||||
def get_all_servers_stats(self):
|
||||
servers = HelperServers.get_all_defined_servers()
|
||||
server_data = []
|
||||
try:
|
||||
for s in servers:
|
||||
# self.select_database(s.get("server_id"))
|
||||
for server in servers:
|
||||
latest = (
|
||||
ServerStats.select()
|
||||
.where(ServerStats.server_id == s.get("server_id"))
|
||||
.where(ServerStats.server_id == server.get("server_id"))
|
||||
.order_by(ServerStats.created.desc())
|
||||
.limit(1)
|
||||
)
|
||||
latest._database = self.database
|
||||
server_data.append(
|
||||
{
|
||||
"server_data": s,
|
||||
"stats": DatabaseShortcuts.return_rows(latest)[0],
|
||||
"server_data": server,
|
||||
"stats": stats,
|
||||
"user_command_permission": True,
|
||||
}
|
||||
)
|
||||
@ -156,7 +153,6 @@ class HelperServerStats:
|
||||
|
||||
def insert_server_stats(self, server):
|
||||
server_id = server.get("id", 0)
|
||||
# self.select_database(server_id)
|
||||
|
||||
if server_id == 0:
|
||||
logger.warning("Stats saving failed with error: Server unknown (id = 0)")
|
||||
@ -172,7 +168,7 @@ class HelperServerStats:
|
||||
ServerStats.mem_percent: server.get("mem_percent", 0),
|
||||
ServerStats.world_name: server.get("world_name", ""),
|
||||
ServerStats.world_size: server.get("world_size", ""),
|
||||
ServerStats.server_port: server.get("server_port", ""),
|
||||
ServerStats.server_port: server.get("server_port", 0),
|
||||
ServerStats.int_ping_results: server.get("int_ping_results", False),
|
||||
ServerStats.online: server.get("online", False),
|
||||
ServerStats.max: server.get("max", False),
|
||||
@ -202,13 +198,12 @@ class HelperServerStats:
|
||||
return {}
|
||||
|
||||
def get_server_stats(self):
|
||||
# self.select_database(self.server_id)
|
||||
stats = (
|
||||
ServerStats.select()
|
||||
.where(ServerStats.server_id == self.server_id)
|
||||
.order_by(ServerStats.created.desc())
|
||||
.limit(1)
|
||||
.get(self.database)
|
||||
.first(self.database)
|
||||
)
|
||||
return model_to_dict(stats)
|
||||
|
||||
@ -261,11 +256,7 @@ class HelperServerStats:
|
||||
.where(ServerStats.server_id == self.server_id)
|
||||
.get(self.database)
|
||||
)
|
||||
# pylint: disable=singleton-comparison
|
||||
if svr.crashed == True:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return svr.crashed
|
||||
|
||||
def set_update(self, value):
|
||||
if self.server_id is None:
|
||||
@ -277,8 +268,9 @@ class HelperServerStats:
|
||||
ServerStats.select().where(ServerStats.server_id == self.server_id).execute(
|
||||
self.database
|
||||
)
|
||||
except Exception as ex:
|
||||
except DoesNotExist as ex:
|
||||
logger.error(f"Database entry not found! {ex}")
|
||||
return
|
||||
ServerStats.update(updating=value).where(
|
||||
ServerStats.server_id == self.server_id
|
||||
).execute(self.database)
|
||||
@ -334,26 +326,24 @@ class HelperServerStats:
|
||||
return last_stat.created - last_stat_with_player.created
|
||||
|
||||
def can_stop_no_players(self, time_limit):
|
||||
# self.select_database(self.server_id)
|
||||
can = False
|
||||
ttl_no_players = self.get_ttl_without_player()
|
||||
if (time_limit == -1) or (ttl_no_players > time_limit):
|
||||
can = True
|
||||
return can
|
||||
return (time_limit == -1) or (ttl_no_players > time_limit)
|
||||
|
||||
def set_waiting_start(self, value):
|
||||
# self.select_database(self.server_id)
|
||||
try:
|
||||
# Checks if server even exists
|
||||
ServerStats.select().where(ServerStats.server_id == self.server_id)
|
||||
except Exception as ex:
|
||||
ServerStats.select().where(ServerStats.server_id == self.server_id).execute(
|
||||
self.database
|
||||
)
|
||||
except DoesNotExist as ex:
|
||||
logger.error(f"Database entry not found! {ex}")
|
||||
return
|
||||
ServerStats.update(waiting_start=value).where(
|
||||
ServerStats.server_id == self.server_id
|
||||
).execute(self.database)
|
||||
|
||||
def get_waiting_start(self):
|
||||
# self.select_database(self.server_id)
|
||||
waiting_start = (
|
||||
ServerStats.select()
|
||||
.where(ServerStats.server_id == self.server_id)
|
||||
|
@ -143,10 +143,10 @@ class HelperServers:
|
||||
@staticmethod
|
||||
def get_server_column(server_id: t.Union[str, int], column_name: str) -> t.Any:
|
||||
column = getattr(Servers, column_name)
|
||||
return model_to_dict(
|
||||
return getattr(
|
||||
Servers.select(column).where(Servers.server_id == server_id).get(),
|
||||
only=[column],
|
||||
)[column_name]
|
||||
column_name,
|
||||
)
|
||||
|
||||
# **********************************************************************************
|
||||
# Servers Methods
|
||||
|
@ -165,19 +165,10 @@ class HelperUsers:
|
||||
@staticmethod
|
||||
def get_user_column(user_id: t.Union[str, int], column_name: str) -> t.Any:
|
||||
column = getattr(Users, column_name)
|
||||
return model_to_dict(
|
||||
return getattr(
|
||||
Users.select(column).where(Users.user_id == user_id).get(),
|
||||
only=[column],
|
||||
)[column_name]
|
||||
|
||||
@staticmethod
|
||||
def check_system_user(user_id):
|
||||
try:
|
||||
result = Users.get(Users.user_id == user_id).user_id == user_id
|
||||
if result:
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
column_name,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_user_model(user_id: str) -> Users:
|
||||
|
@ -65,10 +65,7 @@ class Controller:
|
||||
|
||||
@staticmethod
|
||||
def check_system_user():
|
||||
if HelperUsers.get_user_id_by_name("system") is not None:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return HelperUsers.get_user_id_by_name("system") is not None
|
||||
|
||||
def set_project_root(self, root_dir):
|
||||
self.project_root = root_dir
|
||||
@ -218,12 +215,12 @@ class Controller:
|
||||
+ ("" if empty else f"\nserver-port={port}")
|
||||
)
|
||||
|
||||
server_file = "server.jar" # HACK: Throw this horrible default out of here
|
||||
root_create_data = data[data["create_type"] + "_create_data"]
|
||||
create_data = root_create_data[root_create_data["create_type"] + "_create_data"]
|
||||
if data["create_type"] == "minecraft_java":
|
||||
if root_create_data["create_type"] == "download_jar":
|
||||
server_file = f"{create_data['type']}-{create_data['version']}.jar"
|
||||
full_jar_path = os.path.join(new_server_path, server_file)
|
||||
|
||||
# Create an EULA file
|
||||
with open(
|
||||
@ -234,16 +231,18 @@ class Controller:
|
||||
)
|
||||
elif root_create_data["create_type"] == "import_server":
|
||||
_copy_import_dir_files(create_data["existing_server_path"])
|
||||
full_jar_path = os.path.join(new_server_path, create_data["jarfile"])
|
||||
server_file = create_data["jarfile"]
|
||||
elif root_create_data["create_type"] == "import_zip":
|
||||
# TODO: Copy files from the zip file to the new server directory
|
||||
full_jar_path = os.path.join(new_server_path, create_data["jarfile"])
|
||||
server_file = create_data["jarfile"]
|
||||
raise Exception("Not yet implemented")
|
||||
_create_server_properties_if_needed(create_data["server_properties_port"])
|
||||
|
||||
min_mem = create_data["mem_min"]
|
||||
max_mem = create_data["mem_max"]
|
||||
|
||||
full_jar_path = os.path.join(new_server_path, server_file)
|
||||
|
||||
def _gibs_to_mibs(gibs: float) -> str:
|
||||
return str(int(gibs * 1024))
|
||||
|
||||
@ -273,7 +272,9 @@ class Controller:
|
||||
_create_server_properties_if_needed(0, True)
|
||||
|
||||
server_command = create_data["command"]
|
||||
server_file = ""
|
||||
server_file = (
|
||||
"./bedrock_server" # HACK: This is a hack to make the server start
|
||||
)
|
||||
elif data["create_type"] == "custom":
|
||||
# TODO: working_directory, executable_update
|
||||
if root_create_data["create_type"] == "raw_exec":
|
||||
@ -293,7 +294,11 @@ class Controller:
|
||||
_create_server_properties_if_needed(0, True)
|
||||
|
||||
server_command = create_data["command"]
|
||||
server_file = root_create_data["executable_update"].get("file", "")
|
||||
|
||||
server_file_new = root_create_data["executable_update"].get("file", "")
|
||||
if server_file_new != "":
|
||||
# HACK: Horrible hack to make the server start
|
||||
server_file = server_file_new
|
||||
|
||||
stop_command = data.get("stop_command", "")
|
||||
if stop_command == "":
|
||||
@ -303,7 +308,7 @@ class Controller:
|
||||
log_location = data.get("log_location", "")
|
||||
if log_location == "":
|
||||
# TODO: different default log locations for server creation types
|
||||
log_location = "/logs/latest.log"
|
||||
log_location = "./logs/latest.log"
|
||||
|
||||
if data["monitoring_type"] == "minecraft_java":
|
||||
monitoring_port = data["minecraft_java_monitoring_data"]["port"]
|
||||
@ -315,7 +320,7 @@ class Controller:
|
||||
monitoring_type = "minecraft-bedrock"
|
||||
elif data["monitoring_type"] == "none":
|
||||
# TODO: this needs to be NUKED..
|
||||
# There shouldn't be anything set if there are nothing to monitor
|
||||
# There shouldn't be anything set if there is nothing to monitor
|
||||
monitoring_port = 25565
|
||||
monitoring_host = "127.0.0.1"
|
||||
monitoring_type = "minecraft-java"
|
||||
|
@ -714,6 +714,7 @@ class Server:
|
||||
)
|
||||
# cancel the watcher task
|
||||
self.server_scheduler.remove_job("c_" + str(self.server_id))
|
||||
self.server_scheduler.remove_job("stats_" + str(self.server_id))
|
||||
return
|
||||
|
||||
self.stats_helper.sever_crashed()
|
||||
@ -1143,6 +1144,7 @@ class Server:
|
||||
"desc": raw_ping_result.get("desc"),
|
||||
"version": raw_ping_result.get("version"),
|
||||
"icon": raw_ping_result.get("icon"),
|
||||
"crashed": self.is_crashed,
|
||||
}
|
||||
)
|
||||
if len(self.helper.websocket_helper.clients) > 0:
|
||||
@ -1167,6 +1169,7 @@ class Server:
|
||||
"desc": raw_ping_result.get("desc"),
|
||||
"version": raw_ping_result.get("version"),
|
||||
"icon": raw_ping_result.get("icon"),
|
||||
"crashed": self.is_crashed,
|
||||
},
|
||||
)
|
||||
total_players += int(raw_ping_result.get("online"))
|
||||
|
@ -238,4 +238,4 @@ class BaseHandler(tornado.web.RequestHandler):
|
||||
def finish_json(self, status: int, data: t.Dict[str, t.Any]):
|
||||
self.set_status(status)
|
||||
self.set_header("Content-Type", "application/json")
|
||||
self.finish(orjson.dumps(data)) # pylint: disable=no-member
|
||||
self.finish(orjson.dumps(data))
|
||||
|
@ -16,12 +16,9 @@ from tornado import iostream
|
||||
# TZLocal is set as a hidden import on win pipeline
|
||||
from tzlocal import get_localzone
|
||||
from croniter import croniter
|
||||
from app.classes.controllers.roles_controller import RolesController
|
||||
|
||||
from app.classes.models.roles import HelperRoles
|
||||
from app.classes.models.server_permissions import (
|
||||
EnumPermissionsServer,
|
||||
PermissionsServers,
|
||||
)
|
||||
from app.classes.models.server_permissions import EnumPermissionsServer
|
||||
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
|
||||
from app.classes.models.management import HelpersManagement
|
||||
from app.classes.shared.helpers import Helpers
|
||||
@ -40,8 +37,8 @@ class PanelHandler(BaseHandler):
|
||||
user_roles[user_id] = user_roles_list
|
||||
return user_roles
|
||||
|
||||
def get_role_servers(self) -> t.Set[int]:
|
||||
servers = set()
|
||||
def get_role_servers(self) -> t.List[RolesController.RoleServerJsonType]:
|
||||
servers = []
|
||||
for server in self.controller.servers.list_defined_servers():
|
||||
argument = self.get_argument(f"server_{server['server_id']}_access", "0")
|
||||
if argument == "0":
|
||||
@ -57,7 +54,9 @@ class PanelHandler(BaseHandler):
|
||||
permission_mask, permission, "1"
|
||||
)
|
||||
|
||||
servers.add((server["server_id"], permission_mask))
|
||||
servers.append(
|
||||
{"server_id": server["server_id"], "permissions": permission_mask}
|
||||
)
|
||||
return servers
|
||||
|
||||
def get_perms_quantity(self) -> t.Tuple[str, dict]:
|
||||
@ -2016,35 +2015,7 @@ class PanelHandler(BaseHandler):
|
||||
|
||||
servers = self.get_role_servers()
|
||||
|
||||
# TODO: use update_role_advanced when API v2 gets merged
|
||||
base_data = self.controller.roles.get_role_with_servers(role_id)
|
||||
|
||||
server_ids = {server[0] for server in servers}
|
||||
server_permissions_map = {server[0]: server[1] for server in servers}
|
||||
|
||||
added_servers = server_ids.difference(set(base_data["servers"]))
|
||||
removed_servers = set(base_data["servers"]).difference(server_ids)
|
||||
same_servers = server_ids.intersection(set(base_data["servers"]))
|
||||
logger.debug(
|
||||
f"role: {role_id} +server:{added_servers} -server{removed_servers}"
|
||||
)
|
||||
for server_id in added_servers:
|
||||
PermissionsServers.get_or_create(
|
||||
role_id, server_id, server_permissions_map[server_id]
|
||||
)
|
||||
for server_id in same_servers:
|
||||
PermissionsServers.update_role_permission(
|
||||
role_id, server_id, server_permissions_map[server_id]
|
||||
)
|
||||
if len(removed_servers) != 0:
|
||||
PermissionsServers.delete_roles_permissions(role_id, removed_servers)
|
||||
|
||||
up_data = {
|
||||
"role_name": role_name,
|
||||
"last_update": Helpers.get_time_as_string(),
|
||||
}
|
||||
# TODO: do the last_update on the db side
|
||||
HelperRoles.update_role(role_id, up_data)
|
||||
self.controller.roles.update_role_advanced(role_id, role_name, servers)
|
||||
|
||||
self.controller.management.add_to_audit_log(
|
||||
exec_user["user_id"],
|
||||
@ -2081,10 +2052,7 @@ class PanelHandler(BaseHandler):
|
||||
|
||||
servers = self.get_role_servers()
|
||||
|
||||
role_id = self.controller.roles.add_role(role_name)
|
||||
# TODO: use add_role_advanced when API v2 gets merged
|
||||
for server in servers:
|
||||
PermissionsServers.get_or_create(role_id, server[0], server[1])
|
||||
role_id = self.controller.roles.add_role_advanced(role_name, servers)
|
||||
|
||||
self.controller.management.add_to_audit_log(
|
||||
exec_user["user_id"],
|
||||
|
@ -25,6 +25,9 @@ from app.classes.web.routes.api.servers.server.stats import ApiServersServerStat
|
||||
from app.classes.web.routes.api.servers.server.users import ApiServersServerUsersHandler
|
||||
from app.classes.web.routes.api.users.index import ApiUsersIndexHandler
|
||||
from app.classes.web.routes.api.users.user.index import ApiUsersUserIndexHandler
|
||||
from app.classes.web.routes.api.users.user.permissions import (
|
||||
ApiUsersUserPermissionsHandler,
|
||||
)
|
||||
from app.classes.web.routes.api.users.user.pfp import ApiUsersUserPfpHandler
|
||||
from app.classes.web.routes.api.users.user.public import ApiUsersUserPublicHandler
|
||||
|
||||
@ -58,6 +61,16 @@ def api_handlers(handler_args):
|
||||
ApiUsersUserIndexHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/users/([0-9]+)/permissions/?",
|
||||
ApiUsersUserPermissionsHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/users/(@me)/permissions/?",
|
||||
ApiUsersUserPermissionsHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/users/([0-9]+)/pfp/?",
|
||||
ApiUsersUserPfpHandler,
|
||||
|
@ -1,6 +1,5 @@
|
||||
import datetime
|
||||
import logging
|
||||
from app.classes.shared.console import Console
|
||||
from app.classes.web.base_api_handler import BaseApiHandler
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -12,8 +11,7 @@ class ApiAuthInvalidateTokensHandler(BaseApiHandler):
|
||||
if not auth_data:
|
||||
return
|
||||
|
||||
# TODO: Invalidate tokens
|
||||
Console.info("invalidate_tokens")
|
||||
logger.debug(f"Invalidate tokens for user {auth_data[4]['user_id']}")
|
||||
self.controller.users.raw_update_user(
|
||||
auth_data[4]["user_id"], {"valid_tokens_from": datetime.datetime.now()}
|
||||
)
|
||||
|
@ -79,8 +79,8 @@ class ApiRolesIndexHandler(BaseApiHandler):
|
||||
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||
|
||||
try:
|
||||
data = orjson.loads(self.request.body) # pylint: disable=no-member
|
||||
except orjson.decoder.JSONDecodeError as e: # pylint: disable=no-member
|
||||
data = orjson.loads(self.request.body)
|
||||
except orjson.decoder.JSONDecodeError as e:
|
||||
return self.finish_json(
|
||||
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
|
||||
)
|
||||
|
@ -105,8 +105,8 @@ class ApiRolesRoleIndexHandler(BaseApiHandler):
|
||||
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||
|
||||
try:
|
||||
data = orjson.loads(self.request.body) # pylint: disable=no-member
|
||||
except orjson.decoder.JSONDecodeError as e: # pylint: disable=no-member
|
||||
data = orjson.loads(self.request.body)
|
||||
except orjson.decoder.JSONDecodeError as e:
|
||||
return self.finish_json(
|
||||
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
|
||||
)
|
||||
|
@ -17,24 +17,6 @@ new_server_schema = {
|
||||
"monitoring_type",
|
||||
"create_type",
|
||||
],
|
||||
"examples": [
|
||||
{
|
||||
"name": "My Server",
|
||||
"monitoring_type": "minecraft_java",
|
||||
"minecraft_java_monitoring_data": {"host": "127.0.0.1", "port": 25565},
|
||||
"create_type": "minecraft_java",
|
||||
"minecraft_java_create_data": {
|
||||
"create_type": "download_jar",
|
||||
"download_jar_create_data": {
|
||||
"type": "Paper",
|
||||
"version": "1.18.2",
|
||||
"mem_min": 1,
|
||||
"mem_max": 2,
|
||||
"server_properties_port": 25565,
|
||||
},
|
||||
},
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"title": "Name",
|
||||
@ -665,8 +647,8 @@ class ApiServersIndexHandler(BaseApiHandler):
|
||||
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||
|
||||
try:
|
||||
data = orjson.loads(self.request.body) # pylint: disable=no-member
|
||||
except orjson.decoder.JSONDecodeError as e: # pylint: disable=no-member
|
||||
data = orjson.loads(self.request.body)
|
||||
except orjson.decoder.JSONDecodeError as e:
|
||||
return self.finish_json(
|
||||
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
|
||||
)
|
||||
|
@ -110,7 +110,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
|
||||
setattr(self, key, data[key])
|
||||
setattr(server_obj, key, data[key])
|
||||
self.controller.servers.update_server(server_obj)
|
||||
|
||||
self.controller.management.add_to_audit_log(
|
||||
|
@ -99,7 +99,7 @@ class ApiUsersIndexHandler(BaseApiHandler):
|
||||
email = data.get("email", "default@example.com")
|
||||
enabled = data.get("enabled", True)
|
||||
lang = data.get("lang", self.helper.get_setting("language"))
|
||||
superuser = data.get("superuser", False)
|
||||
new_superuser = data.get("superuser", False)
|
||||
permissions = data.get("permissions", None)
|
||||
roles = data.get("roles", None)
|
||||
hints = data.get("hints", True)
|
||||
@ -134,13 +134,24 @@ class ApiUsersIndexHandler(BaseApiHandler):
|
||||
)
|
||||
permissions_mask = "".join(permissions_mask)
|
||||
|
||||
if new_superuser and not superuser:
|
||||
return self.finish_json(
|
||||
400, {"status": "error", "error": "INVALID_SUPERUSER_CREATE"}
|
||||
)
|
||||
|
||||
if len(roles) != 0 and not superuser:
|
||||
# HACK: This should check if the user has the roles or something
|
||||
return self.finish_json(
|
||||
400, {"status": "error", "error": "INVALID_ROLES_CREATE"}
|
||||
)
|
||||
|
||||
# TODO: do this in the most efficient way
|
||||
user_id = self.controller.users.add_user(
|
||||
username,
|
||||
password,
|
||||
email,
|
||||
enabled,
|
||||
superuser,
|
||||
new_superuser,
|
||||
)
|
||||
self.controller.users.update_user(
|
||||
user_id,
|
||||
|
@ -1,8 +1,13 @@
|
||||
import json
|
||||
import logging
|
||||
import typing as t
|
||||
|
||||
from jsonschema import ValidationError, validate
|
||||
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
|
||||
from app.classes.controllers.users_controller import UsersController
|
||||
from app.classes.models.crafty_permissions import (
|
||||
EnumPermissionsCrafty,
|
||||
PermissionsCrafty,
|
||||
)
|
||||
from app.classes.models.roles import HelperRoles
|
||||
from app.classes.models.users import HelperUsers
|
||||
from app.classes.web.base_api_handler import BaseApiHandler
|
||||
@ -170,7 +175,7 @@ class ApiUsersUserIndexHandler(BaseApiHandler):
|
||||
},
|
||||
)
|
||||
|
||||
if data.get("username", None) is not None:
|
||||
if "username" in data:
|
||||
if data["username"].lower() in ["system", ""]:
|
||||
return self.finish_json(
|
||||
400, {"status": "error", "error": "INVALID_USERNAME"}
|
||||
@ -180,10 +185,10 @@ class ApiUsersUserIndexHandler(BaseApiHandler):
|
||||
400, {"status": "error", "error": "USER_EXISTS"}
|
||||
)
|
||||
|
||||
if data.get("superuser", None) is not None:
|
||||
if str(user["user_id"]) == str(user_id):
|
||||
# Checks if user is trying to change super user status of self.
|
||||
# We don't want that.
|
||||
if "superuser" in data:
|
||||
if str(user["user_id"]) == str(user_id) and not superuser:
|
||||
# Checks if user is trying to change super user status
|
||||
# of self without superuser. We don't want that.
|
||||
return self.finish_json(
|
||||
400, {"status": "error", "error": "INVALID_SUPERUSER_MODIFY"}
|
||||
)
|
||||
@ -191,10 +196,10 @@ class ApiUsersUserIndexHandler(BaseApiHandler):
|
||||
# The user is not superuser so they can't change the superuser status
|
||||
data.pop("superuser")
|
||||
|
||||
if data.get("permissions", None) is not None:
|
||||
if str(user["user_id"]) == str(user_id):
|
||||
# Checks if user is trying to change permissions of self.
|
||||
# We don't want that.
|
||||
if "permissions" in data:
|
||||
if str(user["user_id"]) == str(user_id) and not superuser:
|
||||
# Checks if user is trying to change permissions
|
||||
# of self without superuser. We don't want that.
|
||||
return self.finish_json(
|
||||
400, {"status": "error", "error": "INVALID_PERMISSIONS_MODIFY"}
|
||||
)
|
||||
@ -205,10 +210,10 @@ class ApiUsersUserIndexHandler(BaseApiHandler):
|
||||
400, {"status": "error", "error": "INVALID_PERMISSIONS_MODIFY"}
|
||||
)
|
||||
|
||||
if data.get("roles", None) is not None:
|
||||
if str(user["user_id"]) == str(user_id):
|
||||
# Checks if user is trying to change roles of self.
|
||||
# We don't want that.
|
||||
if "roles" in data:
|
||||
if str(user["user_id"]) == str(user_id) and not superuser:
|
||||
# Checks if user is trying to change roles of
|
||||
# self without superuser. We don't want that.
|
||||
return self.finish_json(
|
||||
400, {"status": "error", "error": "INVALID_ROLES_MODIFY"}
|
||||
)
|
||||
@ -219,10 +224,66 @@ class ApiUsersUserIndexHandler(BaseApiHandler):
|
||||
400, {"status": "error", "error": "INVALID_ROLES_MODIFY"}
|
||||
)
|
||||
|
||||
# TODO: make this more efficient
|
||||
# TODO: add permissions and roles because I forgot
|
||||
if "password" in data and str(user["user_id"] == str(user_id)):
|
||||
# TODO: edit your own password
|
||||
return self.finish_json(
|
||||
400, {"status": "error", "error": "INVALID_PASSWORD_MODIFY"}
|
||||
)
|
||||
|
||||
user_obj = HelperUsers.get_user_model(user_id)
|
||||
|
||||
if "roles" in data:
|
||||
roles: t.Set[str] = set(data.pop("roles"))
|
||||
base_roles: t.Set[str] = set(user_obj.roles)
|
||||
added_roles = roles.difference(base_roles)
|
||||
removed_roles = base_roles.difference(roles)
|
||||
logger.debug(
|
||||
f"updating user {user_id}'s roles: "
|
||||
f"+role:{added_roles} -role:{removed_roles}"
|
||||
)
|
||||
|
||||
for role_id in added_roles:
|
||||
HelperUsers.get_or_create(user_id, role_id)
|
||||
|
||||
if len(removed_roles) != 0:
|
||||
self.controller.users.users_helper.delete_user_roles(
|
||||
user_id, removed_roles
|
||||
)
|
||||
|
||||
if "permissions" in data:
|
||||
permissions: t.List[UsersController.ApiPermissionDict] = data.pop(
|
||||
"permissions"
|
||||
)
|
||||
permissions_mask = "0" * len(EnumPermissionsCrafty)
|
||||
limit_server_creation = 0
|
||||
limit_user_creation = 0
|
||||
limit_role_creation = 0
|
||||
|
||||
for permission in permissions:
|
||||
self.controller.crafty_perms.set_permission(
|
||||
permissions_mask,
|
||||
EnumPermissionsCrafty.__members__[permission["name"]],
|
||||
"1" if permission["enabled"] else "0",
|
||||
)
|
||||
|
||||
PermissionsCrafty.add_or_update_user(
|
||||
user_id,
|
||||
permissions_mask,
|
||||
limit_server_creation,
|
||||
limit_user_creation,
|
||||
limit_role_creation,
|
||||
)
|
||||
|
||||
# TODO: make this more efficient
|
||||
if len(data) != 0:
|
||||
for key in data:
|
||||
# If we don't validate the input there could be security issues
|
||||
value = data[key]
|
||||
if key == "password":
|
||||
value = self.helper.encode_pass(value)
|
||||
setattr(user_obj, key, value)
|
||||
user_obj.save()
|
||||
|
||||
self.controller.management.add_to_audit_log(
|
||||
user["user_id"],
|
||||
(
|
||||
@ -233,9 +294,4 @@ class ApiUsersUserIndexHandler(BaseApiHandler):
|
||||
source_ip=self.get_remote_ip(),
|
||||
)
|
||||
|
||||
for key in data:
|
||||
# If we don't validate the input there could be security issues
|
||||
setattr(user_obj, key, data[key])
|
||||
user_obj.save()
|
||||
|
||||
return self.finish_json(200, {"status": "ok"})
|
||||
|
73
app/classes/web/routes/api/users/user/permissions.py
Normal file
73
app/classes/web/routes/api/users/user/permissions.py
Normal file
@ -0,0 +1,73 @@
|
||||
import logging
|
||||
import typing as t
|
||||
|
||||
from app.classes.models.crafty_permissions import (
|
||||
EnumPermissionsCrafty,
|
||||
PermissionsCrafty,
|
||||
)
|
||||
from app.classes.web.base_api_handler import BaseApiHandler
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
SERVER_CREATION: t.Final[str] = EnumPermissionsCrafty.SERVER_CREATION.name
|
||||
USER_CONFIG: t.Final[str] = EnumPermissionsCrafty.USER_CONFIG.name
|
||||
ROLES_CONFIG: t.Final[str] = EnumPermissionsCrafty.ROLES_CONFIG.name
|
||||
|
||||
|
||||
class ApiUsersUserPermissionsHandler(BaseApiHandler):
|
||||
def get(self, user_id: str):
|
||||
auth_data = self.authenticate_user()
|
||||
if not auth_data:
|
||||
return
|
||||
(
|
||||
_,
|
||||
exec_user_crafty_permissions,
|
||||
_,
|
||||
_,
|
||||
user,
|
||||
) = auth_data
|
||||
|
||||
if user_id in ["@me", user["user_id"]]:
|
||||
user_id = user["user_id"]
|
||||
res_data = PermissionsCrafty.get_user_crafty(user_id)
|
||||
elif EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions:
|
||||
return self.finish_json(
|
||||
400,
|
||||
{
|
||||
"status": "error",
|
||||
"error": "NOT_AUTHORIZED",
|
||||
},
|
||||
)
|
||||
else:
|
||||
# has User_Config permission and isn't viewing self
|
||||
res_data = PermissionsCrafty.get_user_crafty_optional(user_id)
|
||||
if res_data is None:
|
||||
return self.finish_json(
|
||||
404,
|
||||
{
|
||||
"status": "error",
|
||||
"error": "USER_NOT_FOUND",
|
||||
},
|
||||
)
|
||||
|
||||
self.finish_json(
|
||||
200,
|
||||
{
|
||||
"status": "ok",
|
||||
"data": {
|
||||
"permissions": res_data.permissions,
|
||||
"counters": {
|
||||
SERVER_CREATION: res_data.created_server,
|
||||
USER_CONFIG: res_data.created_user,
|
||||
ROLES_CONFIG: res_data.created_role,
|
||||
},
|
||||
"limits": {
|
||||
SERVER_CREATION: res_data.limit_server_creation,
|
||||
USER_CONFIG: res_data.limit_user_creation,
|
||||
ROLES_CONFIG: res_data.limit_role_creation,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
@ -52,12 +52,13 @@
|
||||
</div>
|
||||
<div class="navbar-menu-wrapper d-flex align-items-center">
|
||||
<style>
|
||||
body:not(.sidebar-icon-only) .navbar-toggler .mdi-chevron-double-right {
|
||||
body:not(.sidebar-icon-only) .navbar-toggler .mdi-chevron-double-right {
|
||||
display: none;
|
||||
}
|
||||
body.sidebar-icon-only .navbar-toggler .mdi-chevron-double-left {
|
||||
}
|
||||
|
||||
body.sidebar-icon-only .navbar-toggler .mdi-chevron-double-left {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<button class="navbar-toggler navbar-toggler align-self-center" type="button" data-toggle="minimize">
|
||||
<span class="mdi mdi-chevron-double-left"></span>
|
||||
@ -157,7 +158,8 @@
|
||||
<script src="/static/assets/js/shared/off-canvas.js"></script>
|
||||
<script src="/static/assets/js/shared/hoverable-collapse.js"></script>
|
||||
<script src="/static/assets/js/shared/misc.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.datatables.net/v/bs4/dt-1.10.22/fh-3.1.7/r-2.2.6/sc-2.0.3/sp-1.2.2/datatables.min.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="https://cdn.datatables.net/v/bs4/dt-1.10.22/fh-3.1.7/r-2.2.6/sc-2.0.3/sp-1.2.2/datatables.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/5.4.0/bootbox.min.js"></script>
|
||||
<script type="text/javascript" src="/static/assets/js/motd.js"></script>
|
||||
|
||||
@ -214,14 +216,12 @@
|
||||
};
|
||||
wsInternal.onerror = function (errorEvent) {
|
||||
console.error('WebSocket Error', errorEvent);
|
||||
warn('WebSockets are required for Crafty to work. This websocket connection has been closed. Are you using a reverse proxy? See our'+
|
||||
' documentation for details')
|
||||
warn('WebSockets are required for Crafty to work. This websocket connection has been closed. Are you using a reverse proxy?', 'https://wiki.craftycontrol.com/en/4/docs/Reverse%20Proxy%20Examples')
|
||||
|
||||
};
|
||||
wsInternal.onclose = function (closeEvent) {
|
||||
console.log('Closed WebSocket', closeEvent);
|
||||
warn('WebSockets are required for Crafty to work. This websocket connection has been closed. Are you using a reverse proxy? See our'+
|
||||
' documentation for details')
|
||||
warn('WebSockets are required for Crafty to work. This websocket connection has been closed. Are you using a reverse proxy?', 'https://wiki.craftycontrol.com/en/4/docs/Reverse%20Proxy%20Examples')
|
||||
};
|
||||
|
||||
|
||||
@ -269,11 +269,11 @@
|
||||
}
|
||||
if (webSocket) {
|
||||
webSocket.on('support_status_update', function (logs) {
|
||||
if(logs.percent >= 100){
|
||||
if (logs.percent >= 100) {
|
||||
document.getElementById('logs_progress_bar').innerHTML = '100%';
|
||||
document.getElementById('logs_progress_bar').style.width = '100%';
|
||||
}else{
|
||||
document.getElementById('logs_progress_bar').innerHTML = logs.percent +'%';
|
||||
} else {
|
||||
document.getElementById('logs_progress_bar').innerHTML = logs.percent + '%';
|
||||
document.getElementById('logs_progress_bar').style.width = logs.percent + '%';
|
||||
}
|
||||
});
|
||||
@ -341,7 +341,7 @@
|
||||
}
|
||||
|
||||
function eulaAgree(server_id, command) {
|
||||
//< !--this getCookie function is in base.html-- >
|
||||
//< !--this getCookie function is in base.html-- >
|
||||
var token = getCookie("_xsrf");
|
||||
|
||||
$.ajax({
|
||||
@ -357,7 +357,7 @@
|
||||
}
|
||||
|
||||
|
||||
function warn(message) {
|
||||
function warn(message, link = null) {
|
||||
var closeEl = document.createElement('span');
|
||||
var strongEL = document.createElement('strong');
|
||||
var msgEl = document.createElement('div');
|
||||
@ -383,6 +383,16 @@
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
document.querySelector('.warnings').appendChild(parentEl);
|
||||
}
|
||||
@ -460,4 +470,4 @@
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
@ -24,7 +24,8 @@
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||
<h4 class="card-title"><i class="far fa-code"></i> {{ translate('credits', 'developmentTeam', data['lang']) }}</h4>
|
||||
<h4 class="card-title"><i class="far fa-code"></i> {{ translate('credits', 'developmentTeam', data['lang'])
|
||||
}}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
@ -40,7 +41,8 @@
|
||||
<img src="{{ person['pic'] }}" alt="profile image" class="profile-img img-lg rounded-circle">
|
||||
{% else %}
|
||||
<div alt="profil image" class="profile-img img-lg rounded-circle">
|
||||
<img src="/static/assets/images/credits/user-circle-solid.svg" alt="profile image" class="profile-img img-lg rounded-circle">
|
||||
<img src="/static/assets/images/credits/user-circle-solid.svg" alt="profile image"
|
||||
class="profile-img img-lg rounded-circle">
|
||||
</div>
|
||||
{% end %}
|
||||
</div>
|
||||
@ -66,14 +68,16 @@
|
||||
{% end %}
|
||||
{% if person['tags'][1] %}
|
||||
{% if type(person['tags'][1]) is list %}
|
||||
<a href="{{ person['tags'][1][1] }}" class="btn btn-sm btn-primary mr-2">{{ person['tags'][1][0] }}</a>
|
||||
<a href="{{ person['tags'][1][1] }}" class="btn btn-sm btn-primary mr-2">{{ person['tags'][1][0]
|
||||
}}</a>
|
||||
{% else %}
|
||||
<span class="btn btn-sm btn-primary mr-2">{{ person['tags'][1] }}</span>
|
||||
{% end %}
|
||||
{% end %}
|
||||
{% if person['tags'][2] %}
|
||||
{% if type(person['tags'][2]) is list %}
|
||||
<a href="{{ person['tags'][2][1] }}" class="btn btn-sm btn-inverse-success mr-2">{{ person['tags'][2][0] }}</a>
|
||||
<a href="{{ person['tags'][2][1] }}" class="btn btn-sm btn-inverse-success mr-2">{{
|
||||
person['tags'][2][0] }}</a>
|
||||
{% else %}
|
||||
<span class="btn btn-sm btn-inverse-success mr-2">{{ person['tags'][2] }}</span>
|
||||
{% end %}
|
||||
@ -117,7 +121,8 @@
|
||||
<img src="{{ person['pic'] }}" alt="profile image" class="profile-img img-lg rounded-circle">
|
||||
{% else %}
|
||||
<div alt="profil image" class="profile-img img-lg rounded-circle">
|
||||
<img src="/static/assets/images/credits/user-circle-solid.svg" alt="profile image" class="profile-img img-lg rounded-circle">
|
||||
<img src="/static/assets/images/credits/user-circle-solid.svg" alt="profile image"
|
||||
class="profile-img img-lg rounded-circle">
|
||||
</div>
|
||||
{% end %}
|
||||
</div>
|
||||
@ -143,14 +148,16 @@
|
||||
{% end %}
|
||||
{% if person['tags'][1] %}
|
||||
{% if type(person['tags'][1]) is list %}
|
||||
<a href="{{ person['tags'][1][1] }}" class="btn btn-sm btn-primary mr-2">{{ person['tags'][1][0] }}</a>
|
||||
<a href="{{ person['tags'][1][1] }}" class="btn btn-sm btn-primary mr-2">{{ person['tags'][1][0]
|
||||
}}</a>
|
||||
{% else %}
|
||||
<span class="btn btn-sm btn-primary mr-2">{{ person['tags'][1] }}</span>
|
||||
{% end %}
|
||||
{% end %}
|
||||
{% if person['tags'][2] %}
|
||||
{% if type(person['tags'][2]) is list %}
|
||||
<a href="{{ person['tags'][2][1] }}" class="btn btn-sm btn-inverse-success mr-2">{{ person['tags'][2][0] }}</a>
|
||||
<a href="{{ person['tags'][2][1] }}" class="btn btn-sm btn-inverse-success mr-2">{{
|
||||
person['tags'][2][0] }}</a>
|
||||
{% else %}
|
||||
<span class="btn btn-sm btn-inverse-success mr-2">{{ person['tags'][2] }}</span>
|
||||
{% end %}
|
||||
@ -177,7 +184,8 @@
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||
<h4 class="card-title"><i class="far fa-server"></i> {{ translate('credits', 'retiredStaff', data['lang']) }}</h4>
|
||||
<h4 class="card-title"><i class="far fa-server"></i> {{ translate('credits', 'retiredStaff', data['lang'])
|
||||
}}</h4>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
@ -185,163 +193,180 @@
|
||||
{% for person in data['staff']['retired'] %}
|
||||
<div class="col-lg-6 mb-5">
|
||||
<div class="card rounded shadow-none">
|
||||
<div class="row">
|
||||
<div class="col-md-4" style="max-width: fit-content;">
|
||||
<div class="card-img-top user-avatar mb-auto">
|
||||
{% if person['pic'] %}
|
||||
<img src="{{ person['pic'] }}" alt="profile image" class="profile-img img-lg rounded-circle">
|
||||
{% else %}
|
||||
<div alt="profil image" class="profile-img img-lg rounded-circle">
|
||||
<img src="/static/assets/images/credits/user-circle-solid.svg" alt="profile image">
|
||||
</div>
|
||||
{% end %}
|
||||
</div>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="wrapper d-flex align-items-center">
|
||||
<h4 class="mb-0 font-weight-medium">{{ person['name'] }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="wrapper d-flex align-items-center font-weight-medium text-muted">
|
||||
{% if person['loc'] %}
|
||||
<i class="mdi mdi-map-marker-outline mr-2"></i>
|
||||
<p class="mb-0 text-muted">{{ person['loc'] }}</p>
|
||||
{% end %}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4" style="max-width: fit-content;">
|
||||
<div class="card-img-top user-avatar mb-auto">
|
||||
{% if person['pic'] %}
|
||||
<img src="{{ person['pic'] }}" alt="profile image" class="profile-img img-lg rounded-circle">
|
||||
{% else %}
|
||||
<div alt="profil image" class="profile-img img-lg rounded-circle">
|
||||
<img src="/static/assets/images/credits/user-circle-solid.svg" alt="profile image">
|
||||
</div>
|
||||
{% end %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="wrapper d-flex align-items-start">
|
||||
{% if person['tags'][0] %}
|
||||
<span class="btn btn-sm btn-info mr-2">{{ person['tags'][0] }}</span>
|
||||
{% end %}
|
||||
{% if person['tags'][1] %}
|
||||
{% if type(person['tags'][1]) is list %}
|
||||
<a href="{{ person['tags'][1][1] }}" class="btn btn-sm btn-primary mr-2">{{ person['tags'][1][0] }}</a>
|
||||
{% else %}
|
||||
<span class="btn btn-sm btn-primary mr-2">{{ person['tags'][1] }}</span>
|
||||
{% end %}
|
||||
{% end %}
|
||||
{% if person['tags'][2] %}
|
||||
{% if type(person['tags'][2]) is list %}
|
||||
<a href="{{ person['tags'][2][1] }}" class="btn btn-sm btn-inverse-success mr-2">{{ person['tags'][2][0] }}</a>
|
||||
{% else %}
|
||||
<span class="btn btn-sm btn-inverse-success mr-2">{{ person['tags'][2] }}</span>
|
||||
{% end %}
|
||||
{% end %}
|
||||
<div class="wrapper">
|
||||
<div class="wrapper d-flex align-items-center">
|
||||
<h4 class="mb-0 font-weight-medium">{{ person['name'] }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="wrapper align-items-start pt-3">
|
||||
{% if person['title'] %}
|
||||
<h5><strong>{{ person['title'] }}</strong></h5>
|
||||
<div class="wrapper d-flex align-items-center font-weight-medium text-muted">
|
||||
{% if person['loc'] %}
|
||||
<i class="mdi mdi-map-marker-outline mr-2"></i>
|
||||
<p class="mb-0 text-muted">{{ person['loc'] }}</p>
|
||||
{% end %}
|
||||
<p>{{ person['blurb'] }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="wrapper d-flex align-items-start">
|
||||
{% if person['tags'][0] %}
|
||||
<span class="btn btn-sm btn-info mr-2">{{ person['tags'][0] }}</span>
|
||||
{% end %}
|
||||
{% if person['tags'][1] %}
|
||||
{% if type(person['tags'][1]) is list %}
|
||||
<a href="{{ person['tags'][1][1] }}" class="btn btn-sm btn-primary mr-2">{{ person['tags'][1][0]
|
||||
}}</a>
|
||||
{% else %}
|
||||
<span class="btn btn-sm btn-primary mr-2">{{ person['tags'][1] }}</span>
|
||||
{% end %}
|
||||
{% end %}
|
||||
{% if person['tags'][2] %}
|
||||
{% if type(person['tags'][2]) is list %}
|
||||
<a href="{{ person['tags'][2][1] }}" class="btn btn-sm btn-inverse-success mr-2">{{
|
||||
person['tags'][2][0] }}</a>
|
||||
{% else %}
|
||||
<span class="btn btn-sm btn-inverse-success mr-2">{{ person['tags'][2] }}</span>
|
||||
{% end %}
|
||||
{% end %}
|
||||
</div>
|
||||
|
||||
<div class="wrapper align-items-start pt-3">
|
||||
{% if person['title'] %}
|
||||
<h5><strong>{{ person['title'] }}</strong></h5>
|
||||
{% end %}
|
||||
<p>{{ person['blurb'] }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% end %}
|
||||
</div> <!-- end user row-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-6 grid-margin stretch-card">
|
||||
<div class="card">
|
||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||
<h4 class="card-title"><i class="fab fa-patreon"></i> {{ translate('credits', 'patreonSupporter',
|
||||
data['lang'])
|
||||
}}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="card-description"> {{ translate('credits', 'hugeDesc', data['lang']) }}
|
||||
<code>{{ translate('credits', 'thankYou', data['lang']) }}</code> {{ translate('credits', 'patreonDesc', data['lang']) }} | <span style="color: #9365B8">{{ translate('credits', 'patreonUpdate', data['lang']) }} {{ data["lastUpdate"] }}</span>
|
||||
</p>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ translate('credits', 'patreonName', data['lang']) }}</th>
|
||||
<th>{{ translate('credits', 'patreonLevel', data['lang']) }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for pat in data["patrons"] %}
|
||||
<tr>
|
||||
<td>{{ pat["name"] }}</td>
|
||||
<td>
|
||||
{% if pat["level"] == "Crafty Sustainer" %}
|
||||
<span class="btn btn-sm btn-info mr-2">Sustainer</span>
|
||||
{% elif pat["level"] == "Crafty Advocate" %}
|
||||
<span class="btn btn-sm btn-primary mr-2">Advocate</span>
|
||||
{% elif pat["level"] == "Crafty Supporter" %}
|
||||
<span class="btn btn-sm btn-inverse-success mr-2">Supporter</span>
|
||||
{% else %}
|
||||
<span class="btn btn-sm btn-secondary mr-2">{{ translate('credits', 'patreonOther', data['lang']) }}</span>
|
||||
{% end %}
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6 grid-margin stretch-card">
|
||||
<div class="card">
|
||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||
<h4 class="card-title"><i class="far fa-language"></i> {{ translate('credits', 'translationTitle', data['lang']) }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="card-text"> {{ translate('credits', 'hugeDesc', data['lang']) }}
|
||||
<code>{{ translate('credits', 'thankYou', data['lang']) }}</code> {{ translate('credits', 'translationDesc', data['lang']) }}
|
||||
</p>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ translate('credits', 'translationName', data['lang']) }}</th>
|
||||
<th>{{ translate('credits', 'translator', data['lang']) }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for person in data['translations'] %}
|
||||
<tr>
|
||||
<td>{{ person }}</td>
|
||||
<td class="pb-0">
|
||||
<div class="row">
|
||||
{% for language in data['translations'][person] %}
|
||||
<span class="btn btn-sm btn-inverse-success mr-2" style="margin-bottom: 12px;">{{ language }}</span>
|
||||
{% end %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% end %}
|
||||
</div> <!-- end user row-->
|
||||
</div>
|
||||
</div>
|
||||
<!-- content-wrapper ends -->
|
||||
|
||||
{% end %}
|
||||
<br />
|
||||
|
||||
{% block js %}
|
||||
<script>
|
||||
<div class="row">
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log('ready for JS!')
|
||||
<div class="col-lg-6 grid-margin stretch-card">
|
||||
<div class="card">
|
||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||
<h4 class="card-title"><i class="fab fa-patreon"></i> {{ translate('credits', 'patreonSupporter',
|
||||
data['lang'])
|
||||
}} <i class="fa fa-coffee"></i></h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="card-description"> {{ translate('credits', 'hugeDesc', data['lang']) }}
|
||||
<code>{{ translate('credits', 'thankYou', data['lang']) }}</code> {{ translate('credits',
|
||||
'patreonDesc', data['lang']) }} | <span style="color: #9365B8">{{ translate('credits', 'patreonUpdate',
|
||||
data['lang']) }} {{ data["lastUpdate"] }}</span>
|
||||
</p>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ translate('credits', 'subscriberName', data['lang']) }}</th>
|
||||
<th>{{ translate('credits', 'subscriptionLevel', data['lang']) }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for pat in data["patrons"] %}
|
||||
<tr>
|
||||
<td>{{ pat["name"] }}</td>
|
||||
<td>
|
||||
{% if pat["level"] == "Crafty Sustainer" %}
|
||||
<span class="btn btn-sm btn-info mr-2">Sustainer</span>
|
||||
{% elif pat["level"] == "Crafty Advocate" %}
|
||||
<span class="btn btn-sm btn-primary mr-2">Advocate</span>
|
||||
{% elif pat["level"] == "Crafty Supporter" %}
|
||||
<span class="btn btn-sm btn-inverse-success mr-2">Supporter</span>
|
||||
{% else %}
|
||||
<span class="btn btn-sm btn-secondary mr-2">{{ translate('credits', 'patreonOther', data['lang'])
|
||||
}}</span>
|
||||
{% end %}
|
||||
{% if pat["source"] == "Patreon" %}
|
||||
<span class="badge badge-pill badge-info"><i class="fab fa-patreon"></i> Patreon</span>
|
||||
{% elif pat["source"] == "Ko-fi" %}
|
||||
<span class="badge badge-pill badge-primary"><i class="fa fa-coffee"></i> Ko-fi</span>
|
||||
{% else %}
|
||||
<span class="badge badge-pill badge-dark"><i class="fa fa-question"></i> {{ translate('credits',
|
||||
'patreonOther',
|
||||
data['lang'])
|
||||
}}</span>
|
||||
{% end %}
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
|
||||
});
|
||||
</script>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% end %}
|
||||
<div class="col-lg-6 grid-margin stretch-card">
|
||||
<div class="card">
|
||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||
<h4 class="card-title"><i class="far fa-language"></i> {{ translate('credits', 'translationTitle',
|
||||
data['lang']) }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="card-text"> {{ translate('credits', 'hugeDesc', data['lang']) }}
|
||||
<code>{{ translate('credits', 'thankYou', data['lang']) }}</code> {{ translate('credits',
|
||||
'translationDesc', data['lang']) }}
|
||||
</p>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ translate('credits', 'translationName', data['lang']) }}</th>
|
||||
<th>{{ translate('credits', 'translator', data['lang']) }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for person in data['translations'] %}
|
||||
<tr>
|
||||
<td>{{ person }}</td>
|
||||
<td class="pb-0">
|
||||
<div class="row">
|
||||
{% for language in data['translations'][person] %}
|
||||
<span class="btn btn-sm btn-inverse-success mr-2" style="margin-bottom: 12px;">{{ language }}</span>
|
||||
{% end %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- content-wrapper ends -->
|
||||
|
||||
{% end %}
|
||||
|
||||
{% block js %}
|
||||
<script>
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log('ready for JS!')
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
{% end %}
|
||||
|
@ -39,11 +39,11 @@
|
||||
"hugeDesc": "Ein riesiges",
|
||||
"pageDescription": "Ohne diese Menschen würde Crafty nicht existieren",
|
||||
"pageTitle": "Mitwirkende",
|
||||
"patreonDesc": "an unsere Patreon-Unterstützer!",
|
||||
"patreonLevel": "Unterstützer-Level",
|
||||
"patreonName": "Name",
|
||||
"patreonDesc": "an unsere Patreon/Ko-fi-Unterstützer!",
|
||||
"subscriptionLevel": "Unterstützer-Level",
|
||||
"subscriberName": "Name",
|
||||
"patreonOther": "Andere",
|
||||
"patreonSupporter": "Patreon Unterstützer",
|
||||
"patreonSupporter": "Patreon / Ko-fi Unterstützer",
|
||||
"patreonUpdate": "Letzte Aktualisierung:",
|
||||
"retiredStaff": "Ehemalige Helfer",
|
||||
"supportTeam": "Support- und Dokumentationsteam",
|
||||
|
@ -39,11 +39,11 @@
|
||||
"hugeDesc": "A huge",
|
||||
"pageDescription": "Without these people, you wouldn't have Crafty",
|
||||
"pageTitle": "Credits",
|
||||
"patreonDesc": "to our Patreon supporters!",
|
||||
"patreonLevel": "Level",
|
||||
"patreonName": "Name",
|
||||
"patreonDesc": "to our Patreon / Ko-fi supporters!",
|
||||
"subscriptionLevel": "Level",
|
||||
"subscriberName": "Name",
|
||||
"patreonOther": "Other",
|
||||
"patreonSupporter": "Patreon Supporters",
|
||||
"patreonSupporter": "Patreon / Ko-fi Supporters",
|
||||
"patreonUpdate": "Last Update:",
|
||||
"retiredStaff": "Retired Staff",
|
||||
"supportTeam": "Support and Documentation Team",
|
||||
|
@ -39,11 +39,11 @@
|
||||
"hugeDesc": "Valtava",
|
||||
"pageDescription": "Ilman näitä ihmisiä sinulla ei olisi Craftya",
|
||||
"pageTitle": "Hyvitykset",
|
||||
"patreonDesc": "Patreon-tukijoillemme!",
|
||||
"patreonLevel": "Taso",
|
||||
"patreonName": "Nimi",
|
||||
"patreonDesc": "Patreon / Ko-fi-tukijoillemme!",
|
||||
"subscriptionLevel": "Taso",
|
||||
"subscriberName": "Nimi",
|
||||
"patreonOther": "Muu",
|
||||
"patreonSupporter": "Patreon-tukijat",
|
||||
"patreonSupporter": "Patreon / Ko-fi-tukijat",
|
||||
"patreonUpdate": "Viimeisin päivitys:",
|
||||
"retiredStaff": "Entinen henkilökunta",
|
||||
"supportTeam": "Tuki- ja dokumentointitiimi",
|
||||
|
@ -39,11 +39,11 @@
|
||||
"hugeDesc": "Un Enorme",
|
||||
"pageDescription": "Sans ces personnes, vous n'auriez pas Crafty",
|
||||
"pageTitle": "Crédits",
|
||||
"patreonDesc": "à nos Soutiens Patreon !",
|
||||
"patreonLevel": "Niveau",
|
||||
"patreonName": "Nom",
|
||||
"patreonDesc": "à nos Soutiens Patreon / Ko-fi !",
|
||||
"subscriptionLevel": "Niveau",
|
||||
"subscriberName": "Nom",
|
||||
"patreonOther": "Autre",
|
||||
"patreonSupporter": "Soutiens Patreon",
|
||||
"patreonSupporter": "Soutiens Patreon / Ko-fi",
|
||||
"patreonUpdate": "Dernière mise à Jour :",
|
||||
"retiredStaff": "Retraités de Crafty",
|
||||
"supportTeam": "Equipe de Support et de Documentation",
|
||||
|
@ -39,11 +39,11 @@
|
||||
"hugeDesc": "In enoarm",
|
||||
"pageDescription": "Sûnder dizze minsken soene jo Crafty net hawwe",
|
||||
"pageTitle": "Credits",
|
||||
"patreonDesc": "oan ús Patreon-supporters!",
|
||||
"patreonLevel": "Nivel",
|
||||
"patreonName": "Namme",
|
||||
"patreonDesc": "oan ús Patreon / Ko-fi-supporters!",
|
||||
"subscriptionLevel": "Nivel",
|
||||
"subscriberName": "Namme",
|
||||
"patreonOther": "Oare",
|
||||
"patreonSupporter": "Patreon Supporters",
|
||||
"patreonSupporter": "Patreon / Ko-fi Supporters",
|
||||
"patreonUpdate": "Lêste update:",
|
||||
"retiredStaff": "Pensionearre staff",
|
||||
"supportTeam": "Support- en dokumintaasjeteam",
|
||||
|
532
app/translations/he_IL.json
Normal file
532
app/translations/he_IL.json
Normal file
@ -0,0 +1,532 @@
|
||||
{
|
||||
"404": {
|
||||
"contact": "בבקשה צרו קשר עם תמיכת Crafty Control באמצעות דיסקורד",
|
||||
"notFound": "דף לא נימצא",
|
||||
"unableToFind": "לא הצלחנו למצוא את הדף שאתם מחפשים. בבקשה נסו שוב, או בצעו ריענון."
|
||||
},
|
||||
"accessDenied": {
|
||||
"accessDenied": "גישה נדחתה",
|
||||
"contact": "בבקשה צרו קשר עם תמיכת Crafty Control באמצעות דיסקורד",
|
||||
"contactAdmin": "צרו קשר עם מנהל השרת שלכם לקבלת גישה למשאב זה, או אם אתם חושב שיש לכם גישה למשאב זה, פנו לתמיכה.",
|
||||
"noAccess": "אין לך גישה למשאב זה"
|
||||
},
|
||||
"apiKeys": {
|
||||
"apiKeys": "מפתחות API",
|
||||
"auth": "Authorized? ",
|
||||
"buttons": "כפתורים",
|
||||
"config": "הגדרה",
|
||||
"crafty": "Crafty: ",
|
||||
"created": "Created",
|
||||
"createNew": "צור מפתח API חדש",
|
||||
"deleteKeyConfirmation": "האם ברצונך למחוק מפתח API זה? אי אפשר לבטל את זה.",
|
||||
"deleteKeyConfirmationTitle": "להסיר את מפתח ה-API ${keyId}?",
|
||||
"getToken": "קבלת אסימון",
|
||||
"name": "שם",
|
||||
"nameDesc": "איך תרצו לקרוא לאסימון ה-API הזה? ",
|
||||
"no": "לא",
|
||||
"pageTitle": "ערוך מפתחות API של משתמש",
|
||||
"permName": "שם הגישה",
|
||||
"perms": "גישות",
|
||||
"server": "שרת: ",
|
||||
"superUser": "סופר מתשמש",
|
||||
"yes": "כן"
|
||||
},
|
||||
"base": {
|
||||
"doesNotWorkWithoutJavascript": "<strong>אזהרה: </strong>Crafty לא עובד כשורה כאשר JavaScript אינו מופעל!"
|
||||
},
|
||||
"credits": {
|
||||
"developmentTeam": "צוות פיתוח",
|
||||
"hugeDesc": "תודה ענקית",
|
||||
"pageDescription": "בלי האנשים האלה, לא היה לכם את Crafty",
|
||||
"pageTitle": "קרדיטים",
|
||||
"patreonDesc": "לתומכי הפטראון שלנו!",
|
||||
"subscriptionLevel": "רמה",
|
||||
"subscriberName": "שם",
|
||||
"patreonOther": "אחר",
|
||||
"patreonSupporter": "תומכי פטראון",
|
||||
"patreonUpdate": "העדכון אחרון:",
|
||||
"retiredStaff": "צוות לשעבר",
|
||||
"supportTeam": "צוות תמיכה וכותבי ויקי",
|
||||
"thankYou": "תודה רבה",
|
||||
"translationDesc": "לקהילה שלנו שמתרגמים!",
|
||||
"translationName": "שם",
|
||||
"translationTitle": "שפת התרגום",
|
||||
"translator": "מתרגמים"
|
||||
},
|
||||
"dashboard": {
|
||||
"actions": "פעולות",
|
||||
"allServers": "כל השרתים",
|
||||
"avg": "ממוצע",
|
||||
"backups": "גיבויים",
|
||||
"bePatientClone": "בבקשה חכו בסבלנות בזמן שאנו משכפלים את השרת.<br /> מסך זה יתרענן בעוד רגע",
|
||||
"bePatientRestart": "בבקשה חכו בסבלנות בזמן שאנו מפעילים מחדש את השרת.<br /> מסך זה יתרענן בעוד רגע",
|
||||
"bePatientStart": "בבקשה חכו בסבלנות בזמן שאנו מפעילים את השרת.<br /> מסך זה יתרענן בעוד רגע",
|
||||
"bePatientStop": "בבקשה חכו בסבלנות בזמן שאנו עוצרים את השרת.<br /> מסך זה יתרענן בעוד רגע",
|
||||
"cannotSee": "לא רואים הכל?",
|
||||
"cannotSeeOnMobile": "לא רואים הכל בנייד?",
|
||||
"cannotSeeOnMobile2": "נסו לגלול את הטבלה הצידה.",
|
||||
"clone": "העתק",
|
||||
"cpuCores": "ליבות מעבד",
|
||||
"cpuCurFreq": "שעון נוכחי של מעבד",
|
||||
"cpuMaxFreq": "שעון מרבי של מעבד",
|
||||
"cpuUsage": "שימוש במעבד",
|
||||
"crashed": "קריסות",
|
||||
"dashboard": "פאנל",
|
||||
"delay-explained": "השירות/סוכן התחיל לאחרונה והוא מעכב את הדלקת שרת המיינקראפט",
|
||||
"host": "אחסון",
|
||||
"kill": "כיבוי מידי",
|
||||
"killing": "מכבה מידית...",
|
||||
"lastBackup": "אחרון:",
|
||||
"max": "מקסימום",
|
||||
"memUsage": "שימוש בזיכרון",
|
||||
"motd": "MOTD",
|
||||
"newServer": "צרו שרת חדש",
|
||||
"nextBackup": "הבא:",
|
||||
"no-servers": "כרגע אין שרתים. כדי להתחיל, לחצו",
|
||||
"offline": "סגור",
|
||||
"online": "פעיל",
|
||||
"players": "שחקנים",
|
||||
"restart": "הפעלה מחדש",
|
||||
"sendingCommand": "שולח את הפקודה שלך",
|
||||
"server": "שרת",
|
||||
"servers": "שרתים",
|
||||
"size": "גודל השרת",
|
||||
"start": "התחלה",
|
||||
"starting": "התחלה בעיכוב",
|
||||
"status": "סטאטוס",
|
||||
"stop": "עצור",
|
||||
"version": "גרסה",
|
||||
"welcome": "ברוכים הבאים ל-Crafty Controller"
|
||||
},
|
||||
"datatables": {
|
||||
"i18n": {
|
||||
"aria": {
|
||||
"sortAscending": ": הפעילו כדי למיין עמודות בסדר עולה",
|
||||
"sortDescending": ": הפעילו כדי למיין עמודות בסדר יורד"
|
||||
},
|
||||
"buttons": {
|
||||
"collection": "אוסף <span class='ui-button-icon-primary ui-icon ui-icon-triangle-1-s'/>",
|
||||
"colvis": "נראות עמודה",
|
||||
"colvisRestore": "שיחזור נראות",
|
||||
"copy": "העתק",
|
||||
"copyKeys": "הקש ctrl או u2318 + C כדי להעתיק את נתוני הטבלה ללוח המערכת שלך.<br><br>כדי לבטל, לחצו על הודעה זו או והקישו על escape.",
|
||||
"copySuccess": {
|
||||
"1": "הועתקה שורה אחת ללוח",
|
||||
"_": "הועתקו %d שורות ללוח"
|
||||
},
|
||||
"copyTitle": "העתקה ללוח",
|
||||
"csv": "CSV",
|
||||
"excel": "Excel",
|
||||
"pageLength": {
|
||||
"1": "הצגת שורה אחת",
|
||||
"-1": "הצגת כל השורות",
|
||||
"_": "הצג %d שורות"
|
||||
},
|
||||
"pdf": "PDF",
|
||||
"print": "הדפסה"
|
||||
},
|
||||
"decimal": "",
|
||||
"emptyTable": "אין נתונים זמינים בטבלה",
|
||||
"info": "Showing _START_ to _END_ of _TOTAL_ entries",
|
||||
"infoEmpty": "מציג 0 עד 0 מתוך 0 ערכים",
|
||||
"infoFiltered": "(filtered from _MAX_ total entries)",
|
||||
"infoPostFix": "",
|
||||
"lengthMenu": "Show _MENU_ entries",
|
||||
"loadingRecords": "טוען...",
|
||||
"paginate": {
|
||||
"first": "ראשון",
|
||||
"last": "אחרון",
|
||||
"next": "הבא",
|
||||
"previous": "הקודם"
|
||||
},
|
||||
"processing": "מעבד...",
|
||||
"search": "לחפש:",
|
||||
"select": {
|
||||
"cells": {
|
||||
"0": "לחצו על תא כדי לבחור אותו",
|
||||
"1": "תא %d נבחר",
|
||||
"_": "נבחרו %d תאים"
|
||||
},
|
||||
"columns": {
|
||||
"0": "לחצו על עמודה כדי לבחור בה",
|
||||
"1": "עמודה %d נבחרה",
|
||||
"_": "נבחרו %d עמודות"
|
||||
},
|
||||
"rows": {
|
||||
"0": "לחצו על שורה כדי לבחור בה",
|
||||
"1": "נבחרה שורה %d",
|
||||
"_": "נבחרו %d שורות"
|
||||
}
|
||||
},
|
||||
"thousands": ",",
|
||||
"zeroRecords": "לא נמצאו תוצאות תואמות"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"contact": "בבקשה צרו קשר עם תמיכת Crafty Control באמצעות דיסקורד",
|
||||
"embarassing": "אוי, טוב, זה מביך.",
|
||||
"error": "שגיאה!",
|
||||
"eulaAgree": "אתם מסכימים?",
|
||||
"eulaMsg": "עליכם להסכים להסכם הרישיון למשתמש הקצה. עותק של הסכם הרישיון למשתמש הקצה של מוג'אנג מקושר תחת הודעה זו.",
|
||||
"eulaTitle": "להסכים להסכם רישיון משתמש קצה של מוג'אנג",
|
||||
"hereIsTheError": "הנה השגיאה",
|
||||
"internet": "גילינו שמכונה שמריצה את Crafty אין חיבור לאינטרנט. חיבורי לקוחות לשרת עשויים להיות מוגבלים.",
|
||||
"no-file": "נראה שאיננו מצליחים לאתר את הקובץ המבוקש. בדוק שוב את הנתיב. האם ל-Crafty יש הרשאות מתאימות?",
|
||||
"noJava": "השרת {} לא הצליח להתחיל עם קוד השגיאה: גילינו ש-Java אינו מותקן. אנא התקינו את Java ואז הפעילו את השרת.",
|
||||
"not-downloaded": "לא הצלחנו למצוא את קובץ ההפעלה שלך. האם זה סיים להוריד? האם ההרשאות מוגדרות בשביל הפעלה?",
|
||||
"portReminder": "זיהינו שזו הפעם הראשונה ש-{} מופעל. הקפידו להעביר את היציאה {} דרך הנתב/חומת האש שלכם כדי להפוך אותה לנגישה מרחוק מהאינטרנט.",
|
||||
"start-error": "השרת {} לא הצליח להתחיל עם קוד שגיאה: {}",
|
||||
"terribleFailure": "איזה כישלון נורא!"
|
||||
},
|
||||
"footer": {
|
||||
"allRightsReserved": "כל הזכויות שמורות",
|
||||
"copyright": "זכויות יוצרים",
|
||||
"version": "גרסה"
|
||||
},
|
||||
"login": {
|
||||
"forgotPassword": "שכחתי סיסמה",
|
||||
"login": "התחברות",
|
||||
"password": "סיסמה",
|
||||
"username": "שם משתמש"
|
||||
},
|
||||
"notify": {
|
||||
"activityLog": "יומני פעילות",
|
||||
"backupComplete": "הגיבוי הושלם בהצלחה עבור השרת {}",
|
||||
"backupStarted": "התחיל גיבוי לשרת {}",
|
||||
"downloadLogs": "להוריד יומני תמיכה?",
|
||||
"finishedPreparing": "סיימנו להכין את יומני התמיכה שלך. אנא לחצו על הורדה כדי להוריד",
|
||||
"logout": "להתנתק",
|
||||
"preparingLogs": "אנא המתינו בזמן שאנו מכינים את היומנים שלכם... נשלח הודעה כשהם יהיו מוכנים. זה עשוי להימשך זמן מה לפריסות שרתים גדולות.",
|
||||
"supportLogs": "יומני תמיכה"
|
||||
},
|
||||
"panelConfig": {
|
||||
"adminControls": "בקרות מנהל",
|
||||
"allowedServers": "שרתים מורשים",
|
||||
"assignedRoles": "תפקידים שהוקצו",
|
||||
"cancel": "ביטול",
|
||||
"clearComms": "ניקוי פקודות שלא בוצעו",
|
||||
"delete": "מחיקה",
|
||||
"edit": "עריכה",
|
||||
"enabled": "מופעל",
|
||||
"newRole": "הוספת תפקיד חדש",
|
||||
"newUser": "הוספת משתמש חדש",
|
||||
"pageTitle": "הגדרת פאנל",
|
||||
"role": "תפקיד",
|
||||
"roles": "תפקידים",
|
||||
"roleUsers": "תפקידי משתמשים",
|
||||
"save": "שמירה",
|
||||
"superConfirm": "המשיכו רק אם אתם רוצים שלמשתמש זה תהיה גישה להכל (כל חשבונות המשתמש, השרתים, הגדרות הפאנל וכו'). הם יכולים אפילו למחוק את זכויות משתמש העל שלך.",
|
||||
"superConfirmTitle": "להפעיל משתמש-על? האם אתם בטוחים?",
|
||||
"user": "משתמש",
|
||||
"users": "משתמשים"
|
||||
},
|
||||
"rolesConfig": {
|
||||
"config": "הגדרת תפקיד",
|
||||
"configDesc": "כאן תוכלו לשנות את הגדרות התפקיד הזה",
|
||||
"configUpdate": "עודכן לאחרונה: ",
|
||||
"created": "נוצר: ",
|
||||
"delRole": "מחק תפקיד",
|
||||
"doesNotExist": "אתה לא יכול למחוק משהו שעדיין לא קיים",
|
||||
"pageTitle": "ערוך תפקיד",
|
||||
"pageTitleNew": "תפקיד חדש",
|
||||
"permAccess": "גישה?",
|
||||
"permName": "שם הרשאה",
|
||||
"permsServer": "הרשאות לתפקיד זה עבור השרתים שצוינו",
|
||||
"roleConfigArea": "אזור הגדרת תפקידים",
|
||||
"roleDesc": "איך הייתם רוצים לקרוא לתפקיד הזה?",
|
||||
"roleName": "שם התפקיד: ",
|
||||
"rolePerms": "הרשאות תפקיד",
|
||||
"roleServers": "שרתים מורשים",
|
||||
"roleTitle": "הגדרות תפקידים",
|
||||
"roleUserName": "שם משתמש",
|
||||
"roleUsers": "תפקידי המשתמשים: ",
|
||||
"serverAccess": "גישה?",
|
||||
"serverName": "שם שרת",
|
||||
"serversDesc": "לשרתים מותר לגשת לתפקיד זה"
|
||||
},
|
||||
"serverBackups": {
|
||||
"backupAtMidnight": "גיבוי אוטומטי בחצות?",
|
||||
"backupNow": "גיבוי עכשיו!",
|
||||
"backupTask": "החלה משימת גיבוי.",
|
||||
"cancel": "לבטל",
|
||||
"clickExclude": "לחצו כדי לבחור מה לא יהיה בגיבוי",
|
||||
"compress": "דחוס גיבוי",
|
||||
"confirm": "אישור",
|
||||
"confirmDelete": "האם ברצונכם למחוק את הגיבוי הזה? אי אפשר לבטל את זה.",
|
||||
"confirmRestore": "האם אתם בטוחים שברצונכם לשחזר מגיבוי זה. כל קבצי השרת הנוכחיים ישתנו למצב גיבוי ולא יהיה אפשר לשחזר.",
|
||||
"currentBackups": "גיבויים נוכחיים",
|
||||
"delete": "למחוק",
|
||||
"destroyBackup": "Destroy backup \" + file_to_del + \"?",
|
||||
"download": "הורדה",
|
||||
"excludedBackups": "נתיבים שלא נכללו: ",
|
||||
"excludedChoose": "בחרו את הנתיבים שברצונכם לא לכלול בגיבויים",
|
||||
"exclusionsTitle": "אי הכללות גיבוי",
|
||||
"maxBackups": "מקסימום גיבויים",
|
||||
"maxBackupsDesc": "Crafty לא יאחסן יותר מ-N גיבויים, ימחק את הישן ביותר (הזן 0 כדי לשמור את כולם)",
|
||||
"options": "אפשרויות",
|
||||
"path": "נתיב",
|
||||
"restore": "לשחזר",
|
||||
"restoring": "שחזור גיבוי. זה עשוי לקחת זמן. אנא חכו בסבלנות.",
|
||||
"save": "שמירה",
|
||||
"size": "גודל",
|
||||
"storageLocation": "מקום איחסון",
|
||||
"storageLocationDesc": "איפו אתם רוצים לאחסן גיבויים?"
|
||||
},
|
||||
"serverConfig": {
|
||||
"bePatientDelete": "אנא חכו בסבלנות בזמן שאנו מסירים את השרת שלכם מלוח Crafty. מסך זה ייסגר בעוד מספר רגעים.",
|
||||
"bePatientDeleteFiles": "אנא חכו בסבלנות בזמן שאנו מסירים את השרת שלך מהחלונית Crafty ומוחקים את כל הקבצים. מסך זה ייסגר בעוד מספר רגעים.",
|
||||
"bePatientUpdate": "אנא חכו בסבלנות בזמן שאנו מעדכנים את השרת. זמני ההורדה עשויים להשתנות בהתאם למהירויות האינטרנט שלך.<br /> מסך זה יתרענן בעוד רגע",
|
||||
"cancel": "ביטול",
|
||||
"crashTime": "פסק זמן לקריסה",
|
||||
"crashTimeDesc": "כמה זמן עלינו להמתין לפני שנראה שהשרת שלך קרס?",
|
||||
"deleteFilesQuestion": "למחוק קבצי שרת מהמחשב?",
|
||||
"deleteFilesQuestionMessage": "האם תרצו ש-Crafty תמחק את כל קבצי השרת מהמחשב המארח? <br><br><strong>זה כולל גיבויים של שרתים.</strong>",
|
||||
"deleteServer": "מחיקת שרת",
|
||||
"deleteServerQuestion": "מחיקת שרת?",
|
||||
"deleteServerQuestionMessage": "האם אתם בטוחים שברצונכם למחוק את השרת הזה? אחרי זה אין דרך חזרה...",
|
||||
"exeUpdateURL": "כתובת ה-URL של עדכון השרת הניתן להפעלה",
|
||||
"exeUpdateURLDesc": "כתובת אתר להורדה ישירה לקבלת עדכונים.",
|
||||
"noDelete": "לא, חזרה אחורה",
|
||||
"noDeleteFiles": "לא, פשוט הסר מהפאנל",
|
||||
"removeOldLogsAfter": "הסר יומנים ישנים לאחר",
|
||||
"removeOldLogsAfterDesc": "כמה ימים קובץ יומן צריך להיות ישן כדי להימחק (0 כבוי)",
|
||||
"save": "שמירה",
|
||||
"sendingDelete": "מוחק שרת",
|
||||
"sendingRequest": "שולח את בקשתכם...",
|
||||
"serverAutoStart": "הפעלה אוטומטית של שרת",
|
||||
"serverAutostartDelay": "השהיית הפעלה אוטומטית של השרת",
|
||||
"serverAutostartDelayDesc": "עיכוב לפני הפעלה אוטומטית (אם מופעל למטה)",
|
||||
"serverCrashDetection": "זיהוי קריסת שרת",
|
||||
"serverExecutable": "שרת להפעלה",
|
||||
"serverExecutableDesc": "קובץ ההפעלה של השרת",
|
||||
"serverExecutionCommand": "פקודת ביצוע שרת",
|
||||
"serverExecutionCommandDesc": "מה יושק בטרמינל נסתר",
|
||||
"serverIP": "אייפי של השרת",
|
||||
"serverIPDesc": "Crafty צריך כתובת אייפי בשביל להתחבר לסטטיסטיקה (נסה IP אמיתי במקום 127.0.0.1 אם יש לך בעיות)",
|
||||
"serverLogLocation": "מיקום יומן שרת",
|
||||
"serverLogLocationDesc": "נתיב מלא לקובץ היומן",
|
||||
"serverName": "שם שרת",
|
||||
"serverNameDesc": "איך אתם רוצים לקרוא לשרת הזה",
|
||||
"serverPath": "ספריית עבודה של שרת",
|
||||
"serverPathDesc": "נתיב מלא מוחלט (לא כולל קובץ הפעלה)",
|
||||
"serverPort": "פורט שרת",
|
||||
"serverPortDesc": "Crafty צריך פורט בשביל להתחבר לנתונים סטטיסטיים",
|
||||
"serverStopCommand": "פקודת עצירת שרת",
|
||||
"serverStopCommandDesc": "פקודה לשלוח את התוכנית כדי לעצור אותה",
|
||||
"stopBeforeDeleting": "בבקשה לעצור את השרת לפני מחיקתו",
|
||||
"update": "עדכנו את קובץ ההפעלה",
|
||||
"yesDelete": "כן, למחוק",
|
||||
"yesDeleteFiles": "כן, מחק קבצים"
|
||||
},
|
||||
"serverConfigHelp": {
|
||||
"desc": "כאן אתם יכולים לשנות את הגדרות השרת שלכם",
|
||||
"perms": [
|
||||
"מומלץ <code>לא</code> לשנות את הנתיבים של שרת המנוהל על ידי Crafty.",
|
||||
"שינוי נתיבים <code>יכול</code> לשבור דברים, במיוחד במערכות הפעלה מסוג לינוקס שבהן הרשאות הקבצים נעולות יותר.",
|
||||
"<br /><br/>",
|
||||
"אם אתם מרגישים צורך לשנות את מיקום השרת, אתם רשאים לעשות זאת כל עוד אתם נותנים למשתמש \"crafty\" הרשאת קריאה / כתיבה לנתיב השרת.",
|
||||
"<br />",
|
||||
"<br />",
|
||||
"בלינוקס זה נעשה בצורה הטובה ביותר על ידי ביצוע הפעולות הבאות:<br />",
|
||||
"<code>",
|
||||
" sudo chown crafty:crafty /path/to/your/server -R<br />",
|
||||
" sudo chmod 2775 /path/to/your/server -R<br />",
|
||||
"</code>"
|
||||
],
|
||||
"title": "אזור הגדרת השרת"
|
||||
},
|
||||
"serverDetails": {
|
||||
"backup": "גיבוי",
|
||||
"config": "הגדרות",
|
||||
"files": "קבצים",
|
||||
"logs": "לוג",
|
||||
"playerControls": "ניהול שחקנים",
|
||||
"schedule": "לוח זמנים",
|
||||
"serverDetails": "פרטי שרת",
|
||||
"terminal": "מסוף פקודות"
|
||||
},
|
||||
"serverFiles": {
|
||||
"clickUpload": "לחץ כאן כדי לבחור את הקבצים שלך",
|
||||
"close": "סגור",
|
||||
"createDir": "ליצור תיקייה",
|
||||
"createDirQuestion": "איזה שם אתם רוצים לספרייה החדשה?",
|
||||
"createFile": "צור קובץ",
|
||||
"createFileQuestion": "איזה שם אתם רוצים לקובץ החדש?",
|
||||
"default": "ברירת מחדל",
|
||||
"delete": "למחוק",
|
||||
"deleteItemQuestion": "Are you sure you want to delete \" + name + \"?",
|
||||
"deleteItemQuestionMessage": "You are deleting \\\"\" + path + \"\\\"!<br/><br/>This action will be irreversible and it'll be lost forever!",
|
||||
"download": "הורדה",
|
||||
"editingFile": "עריכת קובץ",
|
||||
"error": "שגיאה בעת קבלת קבצים",
|
||||
"fileReadError": "שגיאת קריאת קובץ",
|
||||
"files": "קבצים",
|
||||
"keybindings": "מקשי קיצור",
|
||||
"loadingRecords": "טוען קבצים...",
|
||||
"noDelete": "לא",
|
||||
"noscript": "מנהל הקבצים לא עובד ללא JavaScript",
|
||||
"rename": "שנה שם",
|
||||
"renameItemQuestion": "מה צריך להיות השם החדש?",
|
||||
"save": "שמור",
|
||||
"stayHere": "אל תצאומדף הזה!!",
|
||||
"unsupportedLanguage": "אזהרה: סוג קובץ נתמך",
|
||||
"unzip": "פתיחת קובץ מכווץ",
|
||||
"upload": "העלה",
|
||||
"uploadTitle": "העלת קבצים אל: ",
|
||||
"waitUpload": "אנא המתן בזמן שאנו מעלים את הקבצים שלך... זה עשוי לקחת זמן מה.",
|
||||
"yesDelete": "כן, אני מבין.ה את ההשלכות"
|
||||
},
|
||||
"serverPlayerManagement": {
|
||||
"bannedPlayers": "שחקנים בבאן",
|
||||
"loadingBannedPlayers": "טוען שחקנים שהם בבאן",
|
||||
"players": "שחקנים"
|
||||
},
|
||||
"serverScheduleConfig": {
|
||||
"backup": "גיבוי שרת",
|
||||
"basic": "בסיסי",
|
||||
"children": "משימות מקושרות: ",
|
||||
"command": "פקודה",
|
||||
"command-explain": "איזו פקודה אתם רוצים שנבצע? אל תכללו את ה-'/'",
|
||||
"cron": "קרון",
|
||||
"cron-explain": "הזן את מחרוזת הקרון שלך",
|
||||
"custom": "פקודה מותאמת אישית",
|
||||
"days": "ימים",
|
||||
"enabled": "מופעל",
|
||||
"hours": "שעות",
|
||||
"interval": "הפסקה",
|
||||
"interval-explain": "באיזו תדירות אתם רוצים לשלוח זמנים אם זה יתבצע?",
|
||||
"minutes": "דקות",
|
||||
"offset": "קיזוז עיכוב",
|
||||
"offset-explain": "כמה זמן עלינו לחכות כדי לשגר את זה לאחר שיגור המשימה הראשונה? (שניות)",
|
||||
"one-time": "מחק לאחר ביצוע",
|
||||
"parent": "בחר לוח זמנים להורים",
|
||||
"parent-explain": "איזה לוח זמנים צריך להפעיל את זה?",
|
||||
"reaction": "תגובה",
|
||||
"restart": "הפעלה מחדש",
|
||||
"start": "הדלקת שרת",
|
||||
"stop": "כיבוי שרת",
|
||||
"time": "זמן",
|
||||
"time-explain": "באיזו שעה אתה רוצה שהלוח שלך יתבצע?"
|
||||
},
|
||||
"serverSchedules": {
|
||||
"areYouSure": "למחוק משימה מתוזמנת?",
|
||||
"cancel": "לבטל",
|
||||
"cannotSee": "לא רואים הכל?",
|
||||
"cannotSeeOnMobile": "נסה ללחוץ על משימה מתוזמנת לפרטים מלאים.",
|
||||
"confirm": "אישור",
|
||||
"confirmDelete": "האם ברצונך למחוק את המשימה המתוזמנת הזו? אי אפשר לבטל את זה."
|
||||
},
|
||||
"serverStats": {
|
||||
"cpuUsage": "שימוש במעבד",
|
||||
"description": "תיאור",
|
||||
"errorCalculatingUptime": "שגיאה בחישוב זמן פעולה",
|
||||
"memUsage": "שימוש בזיכרון",
|
||||
"offline": "לא מקוון",
|
||||
"online": "פועל",
|
||||
"players": "שחקנים",
|
||||
"serverStarted": "השרת התחיל",
|
||||
"serverStatus": "סטטוס שרת",
|
||||
"serverTime": "איזור זמן",
|
||||
"serverTimeZone": "אזור זמן של שרת",
|
||||
"serverUptime": "זמן פעילות שרת",
|
||||
"starting": "התחלה מאוחרת",
|
||||
"unableToConnect": "לא מצליח להתחבר",
|
||||
"version": "גרסה"
|
||||
},
|
||||
"serverTerm": {
|
||||
"commandInput": "הקלידו את הפקודה שלכם",
|
||||
"delay-explained": "The service/agent has recently started and is delaying the start of the minecraft server instance",
|
||||
"downloading": "מוריד...",
|
||||
"restart": "הפעלה מחדש",
|
||||
"sendCommand": "שליחת פקודה",
|
||||
"start": "התחלה",
|
||||
"starting": "התחלה מאוחרת",
|
||||
"stop": "עצור",
|
||||
"stopScroll": "לעצור את הגלילה האוטומטית",
|
||||
"updating": "מתעדכן..."
|
||||
},
|
||||
"serverWizard": {
|
||||
"absoluteServerPath": "נתיב מוחלט לשרת שלך",
|
||||
"absoluteZipPath": "נתיב מוחלט לשרת שלך",
|
||||
"addRole": "הוסף שרת לתפקידים קיימים",
|
||||
"autoCreate": "אם אף אחד לא נבחר, Crafty תעשה אחד!",
|
||||
"bePatient": "אנא התאזר בסבלנות בזמן שאנו ' + (מייבאים? 'ייבוא': 'מורידים') + ' השרת",
|
||||
"buildServer": "בניית שרת!",
|
||||
"clickRoot": "לחץ כאן כדי לבחור Root Dir",
|
||||
"close": "סגור",
|
||||
"defaultPort": "25565 ברירת מחדל",
|
||||
"downloading": "מוריד שרת...",
|
||||
"explainRoot": "אנא לחץ על הלחצן למטה כדי לבחור את ה-root dir של השרת שלך בתוך הארכיון",
|
||||
"importing": "מייבא שרת...",
|
||||
"importServer": "ייבוא שרת קיים",
|
||||
"importServerButton": "ייבוא שרת!",
|
||||
"importZip": "ייבוא מקובץ Zip",
|
||||
"maxMem": "מקסימום זיכרון",
|
||||
"minMem": "מינימום זיכרון",
|
||||
"myNewServer": "השרת החדש שלי",
|
||||
"newServer": "צור שרת חדש",
|
||||
"quickSettings": "הגדרות מהירות",
|
||||
"quickSettingsDescription": "אל תדאג, אתה יכול לשנות את אלה מאוחר יותר",
|
||||
"resetForm": "אפס טופס",
|
||||
"save": "שמור",
|
||||
"selectRole": "בחר תפקידים",
|
||||
"selectRoot": "בחר ארכיון שורש Dir",
|
||||
"selectType": "בחר סוג",
|
||||
"selectVersion": "בחר גרסה",
|
||||
"selectZipDir": "בחר את הספרייה בארכיון שממנו אתה רוצה שנפתח קבצים",
|
||||
"serverJar": "קובץ שרת להפעלה",
|
||||
"serverName": "שם השרת",
|
||||
"serverPath": "נתיב שרת",
|
||||
"serverPort": "פורט שרת",
|
||||
"serverType": "סוג השרת",
|
||||
"serverVersion": "גרסת השרת",
|
||||
"sizeInGB": "גודל ב-GB",
|
||||
"zipPath": "נתיב שרת"
|
||||
},
|
||||
"sidebar": {
|
||||
"contribute": "לתרום",
|
||||
"credits": "קרדיט",
|
||||
"dashboard": "פאנל",
|
||||
"documentation": "ויקיפדייה",
|
||||
"navigation": "ניווט",
|
||||
"newServer": "צור שרת חדש",
|
||||
"servers": "שרתים"
|
||||
},
|
||||
"userConfig": {
|
||||
"apiKey": "מפתחות API",
|
||||
"auth": "מורשה? ",
|
||||
"config": "הגדרות",
|
||||
"configArea": "אזור הגדרות משתמש",
|
||||
"configAreaDesc": "כאן אתה משנה את כל הגדרות המשתמש שלך",
|
||||
"confirmDelete": "האם אתה בטוח שברצונך למחוק משתמש זה? פעולה זו היא בלתי הפיכה.",
|
||||
"craftyPermDesc": "הרשאות Crafty יש למשתמש הזה ",
|
||||
"craftyPerms": "Crafty הרשאות: ",
|
||||
"created": "נוצר: ",
|
||||
"deleteUser": "מחק משתמש: ",
|
||||
"deleteUserB": "מחק משתמש",
|
||||
"delSuper": "אינך יכול למחוק משתמש-על",
|
||||
"enabled": "מופעל",
|
||||
"gravDesc": "אימייל זה מיועד אך ורק לשימוש עם Gravatar™. Crafty לא תשתמש בשום מקרה באימייל זה לשום דבר מלבד חיפוש Gravatar™ שלך",
|
||||
"gravEmail": "אימייל Gravatar™",
|
||||
"lastIP": "אייפי אחרון: ",
|
||||
"lastLogin": "כניסה אחרונה: ",
|
||||
"lastUpdate": "עדכון אחרון: ",
|
||||
"leaveBlank": "כדי לערוך משתמש מבלי לשנות סיסמה השאר אותו ריק.",
|
||||
"member": "חבר?",
|
||||
"notExist": "אתה לא יכול למחוק משהו שלא קיים!",
|
||||
"pageTitle": "ערוך משתמש",
|
||||
"pageTitleNew": "צור משתמש",
|
||||
"password": "סיסמה",
|
||||
"permName": "שם הרשאה",
|
||||
"repeat": "חזור על הסיסמה",
|
||||
"roleName": "שם התפקיד",
|
||||
"super": "משתמש על",
|
||||
"userLang": "שפת משתמש",
|
||||
"userName": "שם משתמש",
|
||||
"userNameDesc": "איך אתה רוצה לקרוא למשתמש הזה?",
|
||||
"userRoles": "תפקידי משתמש",
|
||||
"userRolesDesc": "תפקידים שמשתמש זה חבר בהם",
|
||||
"userSettings": "הגדרות משתמש",
|
||||
"uses": "מספר השימושים המותרים (-1==ללא הגבלה)"
|
||||
}
|
||||
}
|
@ -39,11 +39,11 @@
|
||||
"hugeDesc": "Veliko",
|
||||
"pageDescription": "Bez ovih ljudi ne biste imali Crafty",
|
||||
"pageTitle": "Zasluge",
|
||||
"patreonDesc": "našim Patreon donorima!",
|
||||
"patreonLevel": "Razina",
|
||||
"patreonName": "Ime",
|
||||
"patreonDesc": "našim Patreon / Ko-fi donorima!",
|
||||
"subscriptionLevel": "Razina",
|
||||
"subscriberName": "Ime",
|
||||
"patreonOther": "Ostalo",
|
||||
"patreonSupporter": "Patreon donorima",
|
||||
"patreonSupporter": "Patreon / Ko-fi donorima",
|
||||
"patreonUpdate": "Zadnje ažuriranje:",
|
||||
"retiredStaff": "Umirovljeno osoblje",
|
||||
"supportTeam": "Tim za podršku i dokumentaciju",
|
||||
|
@ -39,11 +39,11 @@
|
||||
"hugeDesc": "Besar",
|
||||
"pageDescription": "Tanpa orang-orang ini, Anda tidak akan memiliki Crafty",
|
||||
"pageTitle": "Kredit",
|
||||
"patreonDesc": "untuk pendukung Patreon kami!",
|
||||
"patreonLevel": "Level",
|
||||
"patreonName": "Nama",
|
||||
"patreonDesc": "untuk pendukung Patreon / Ko-fi kami!",
|
||||
"subscriptionLevel": "Level",
|
||||
"subscriberName": "Nama",
|
||||
"patreonOther": "Lainnya",
|
||||
"patreonSupporter": "Suporter Patreon",
|
||||
"patreonSupporter": "Suporter Patreon / Ko-fi",
|
||||
"patreonUpdate": "Pembaharuan Terakhir:",
|
||||
"retiredStaff": "Pensiunan Staf",
|
||||
"supportTeam": "Tim Dukungan dan Dokumentasi",
|
||||
|
@ -39,11 +39,11 @@
|
||||
"hugeDesc": "Un enorme",
|
||||
"pageDescription": "Senza queste persone, non avremmo Crafty",
|
||||
"pageTitle": "Crediti",
|
||||
"patreonDesc": "ai nostri supporter di Patreon!",
|
||||
"patreonLevel": "Livello",
|
||||
"patreonName": "Nome",
|
||||
"patreonDesc": "ai nostri supporter di Patreon / Ko-fi!",
|
||||
"subscriptionLevel": "Livello",
|
||||
"subscriberName": "Nome",
|
||||
"patreonOther": "Altro",
|
||||
"patreonSupporter": "Supporter Patreon",
|
||||
"patreonSupporter": "Supporter Patreon / Ko-fi",
|
||||
"patreonUpdate": "Ultimo aggiornamento:",
|
||||
"retiredStaff": "Staff ritirato",
|
||||
"supportTeam": "Squadra di supporto e documentazione",
|
||||
|
@ -39,11 +39,11 @@
|
||||
"hugeDesc": "A HOOJ",
|
||||
"pageDescription": "WITHOUT THEES PEEPS, U WOULDNT HAS CWAFTY",
|
||||
"pageTitle": "GUD HOOMANZ",
|
||||
"patreonDesc": "2 DA PATREON SUPPORTERS!",
|
||||
"patreonLevel": "LVLZ",
|
||||
"patreonName": "NAMEZ",
|
||||
"patreonDesc": "2 DA PATREUN UN KOFEE SUPPORTERS!",
|
||||
"subscriptionLevel": "LVLZ",
|
||||
"subscriberName": "NAMEZ",
|
||||
"patreonOther": "OTHERZ",
|
||||
"patreonSupporter": "PATREON SUPPORTERS",
|
||||
"patreonSupporter": "PATREUN UN KOFEE SUPPORTERS",
|
||||
"patreonUpdate": "LAST UPDATE:",
|
||||
"retiredStaff": "DISTANT PPLZ, NEVR FORGOTTEN",
|
||||
"supportTeam": "THEES PEEOPLE FED AN CARE 4 ME, THEY VRY VRY GUD PPL",
|
||||
|
@ -39,11 +39,11 @@
|
||||
"hugeDesc": "Liels",
|
||||
"pageDescription": "Bez šiem cilvēkiem, jums nebūtu Crafty",
|
||||
"pageTitle": "Pateicības",
|
||||
"patreonDesc": "mūsu Patreon atbalstītājiem!",
|
||||
"patreonLevel": "Līmenis",
|
||||
"patreonName": "Vārds",
|
||||
"patreonDesc": "mūsu Patreon / Ko-fi atbalstītājiem!",
|
||||
"subscriptionLevel": "Līmenis",
|
||||
"subscriberName": "Vārds",
|
||||
"patreonOther": "Cits",
|
||||
"patreonSupporter": "Patreon Atbalstītāji",
|
||||
"patreonSupporter": "Patreon / Ko-fi Atbalstītāji",
|
||||
"patreonUpdate": "Pēdējais Atjaunojums:",
|
||||
"retiredStaff": "Atvaļinātie Komandas Biedri",
|
||||
"supportTeam": "Atbalsta un Dokumentācijas Komanda",
|
||||
|
@ -39,11 +39,11 @@
|
||||
"hugeDesc": "Een enorme",
|
||||
"pageDescription": "Zonder deze mensen, zou je Crafty niet hebben",
|
||||
"pageTitle": "Credits",
|
||||
"patreonDesc": "aan onze Patreon supporters!",
|
||||
"patreonLevel": "Niveau",
|
||||
"patreonName": "Naam",
|
||||
"patreonDesc": "aan onze Patreon / Ko-fi supporters!",
|
||||
"subscriptionLevel": "Niveau",
|
||||
"subscriberName": "Naam",
|
||||
"patreonOther": "Andere",
|
||||
"patreonSupporter": "Patreon supporters",
|
||||
"patreonSupporter": "Patreon / Ko-fi supporters",
|
||||
"patreonUpdate": "Laatste Update:",
|
||||
"retiredStaff": "Gepensioneerd personeel",
|
||||
"supportTeam": "Ondersteunings- en documentatieteam",
|
||||
|
@ -39,11 +39,11 @@
|
||||
"hugeDesc": "Een gigantische",
|
||||
"pageDescription": "Zonder deze mensen zou er geen Crafty zijn",
|
||||
"pageTitle": "Credits",
|
||||
"patreonDesc": "aan onze Patreon supporters!",
|
||||
"patreonLevel": "Niveau",
|
||||
"patreonName": "Naam",
|
||||
"patreonDesc": "aan onze Patreon / Ko-fi supporters!",
|
||||
"subscriptionLevel": "Niveau",
|
||||
"subscriberName": "Naam",
|
||||
"patreonOther": "Overig",
|
||||
"patreonSupporter": "Patreon Supporters",
|
||||
"patreonSupporter": "Patreon / Ko-fi Supporters",
|
||||
"patreonUpdate": "Laatste update:",
|
||||
"retiredStaff": "Gepensioneerde staff",
|
||||
"supportTeam": "Support- en documentatieteam",
|
||||
|
@ -39,11 +39,11 @@
|
||||
"hugeDesc": "非常",
|
||||
"pageDescription": "没有这些人,就没有 Crafty",
|
||||
"pageTitle": "鸣谢",
|
||||
"patreonDesc": "我们的 Patreon 支持者!",
|
||||
"patreonLevel": "等级",
|
||||
"patreonName": "名称",
|
||||
"patreonDesc": "我们的 Patreon / Ko-fi 支持者!",
|
||||
"subscriptionLevel": "等级",
|
||||
"subscriberName": "名称",
|
||||
"patreonOther": "其他",
|
||||
"patreonSupporter": "Patreon 支持者",
|
||||
"patreonSupporter": "Patreon / Ko-fi 支持者",
|
||||
"patreonUpdate": "上次更新:",
|
||||
"retiredStaff": "退休员工",
|
||||
"supportTeam": "支持与文档团队",
|
||||
|
@ -1,5 +1,5 @@
|
||||
#Base config made by Justman10000 and Zedifus (https://gitlab.com/Zedifus)
|
||||
#Adapted for WSS by Andrew McManus https://gitlab.com/amcmanu3
|
||||
#Adapted for WSS by pretzelDewey https://gitlab.com/amcmanu3
|
||||
#For this config you need to add the following mods
|
||||
#mod_ssl
|
||||
#mod_rewrite
|
||||
|
@ -17,5 +17,5 @@ requests==2.26
|
||||
termcolor==1.1
|
||||
tornado==6.0
|
||||
tzlocal==4.0
|
||||
jsonschema==4.4.0
|
||||
jsonschema==4.5.1
|
||||
orjson==3.6.7
|
||||
|
Loading…
Reference in New Issue
Block a user