Merge branch 'dev' into 'master'

v4.3.2

See merge request crafty-controller/crafty-4!748
This commit is contained in:
Iain Powrie 2024-04-07 00:26:17 +00:00
commit 6652541876
37 changed files with 897 additions and 401 deletions

View File

@ -3,16 +3,16 @@
- **Install Type:** Git Cloned(Manual) / Installer / WinPackage / Docker - **Install Type:** Git Cloned(Manual) / Installer / WinPackage / Docker
## What Happened? ## What Happened?
*A brief description of what happened when you tried to perform an action* <!-- A brief description of what happened when you tried to perform an action -->
## Expected result ## Expected result
*What should have happened when you performed the actions* <!-- What should have happened when you performed the actions -->
## Steps to reproduce ## Steps to reproduce
*List the steps required to produce the error. These should be as few as possible* <!-- List the steps required to produce the error. These should be as few as possible -->
## Screenshots ## Screenshots
Any relevant screenshots which show the issue* !-->* <!-- Any relevant screenshots which show the issue -->
## Priority/Severity ## Priority/Severity
- [ ] High (anything that impacts the normal user flow or blocks app usage) - [ ] High (anything that impacts the normal user flow or blocks app usage)

View File

@ -1,13 +1,14 @@
## Summary ## Summary
*Outline the issue being faced, and why this needs to change* <!-- Outline the issue being faced, and why this needs to change -->
## Area of the system ## Area of the system
*This might only be one part, but may involve multiple sections, Login/Dashboad/Terminal/Config* <!-- This might only be one part, but may involve multiple sections, Login/Dashboad/Terminal/Config -->
## How does this currently work? ## How does this currently work?
<!-- A brief description of how the functionality currently operates -->
## What is the desired way of working? ## What is the desired way of working?
*After the change, what should the process/operation be?* <!-- After the change, what should the process/operation be? -->
## Priority/Severity ## Priority/Severity
- [ ] High (This will bring a huge increase in performance/productivity/usability) - [ ] High (This will bring a huge increase in performance/productivity/usability)

View File

@ -1,8 +1,8 @@
## Problem Statement ## Problem Statement
*What is the issue being faced and needs addressing?* <!-- What is the issue being faced and needs addressing? -->
## Who will benefit? ## Who will benefit?
*Will this fix a problem that only one user has, or will it benefit a lot of people* <!-- Will this fix a problem that only one user has, or will it benefit a lot of people -->
## Benefits and risks ## Benefits and risks
What benefits does this bring? What benefits does this bring?
@ -16,10 +16,10 @@
## Proposed solution ## Proposed solution
*How would you like to see this issue resolved?* <!-- How would you like to see this issue resolved? -->
## Examples ## Examples
*Are there any examples of this which exist in other software?* <!-- Are there any examples of this which exist in other software? -->
## Priority/Severity ## Priority/Severity
- [ ] High (This will bring a huge increase in performance/productivity/usability) - [ ] High (This will bring a huge increase in performance/productivity/usability)

View File

@ -1,22 +1,22 @@
## What does this MR do and why? ## What does this MR do and why?
___Describe in detail what your merge request does and why.___<br> <!-- Describe in detail what your merge request does and why. -->
> *Please keep this description updated with any discussion that takes place so*<br> <!-- Please keep this description updated with any discussion that takes place so -->
*that reviewers can understand your intent. Keeping the description updated is*<br> <!-- that reviewers can understand your intent. Keeping the description updated is -->
*especially important if they didn't participate in the discussion.*<br> <!-- especially important if they didn't participate in the discussion. -->
## Screenshots or screen recordings ## Screenshots or screen recordings
___These are strongly recommended to assist reviewers and reduce the time to merge your change.___<br> <!-- These are strongly recommended to assist reviewers and reduce the time to merge your change. -->
> *Please include any relevant screenshots or screen recordings that will assist*<br> <!-- Please include any relevant screenshots or screen recordings that will assist, -->
*reviewers and future readers. If you need help visually verifying the change,*<br> <!-- reviewers and future readers. If you need help visually verifying the change, -->
*please leave a comment and ping a GitLab reviewer, maintainer, or MR coach.*<br> <!-- please leave a comment and ping a GitLab reviewer, maintainer, or MR coach. -->
## How to set up and validate locally ## How to set up and validate locally
___Numbered steps to set up and validate the change are strongly suggested.___ <!-- Numbered steps to set up and validate the change are strongly suggested. -->
## MR acceptance checklist ## MR acceptance checklist

View File

@ -3,13 +3,46 @@
# Prompt the user for the directory path # Prompt the user for the directory path
read -p "Enter the directory path to set permissions (/var/opt/minecraft/crafty): " directory_path read -p "Enter the directory path to set permissions (/var/opt/minecraft/crafty): " directory_path
# Count the total number of directories
total_dirs=$(find "$directory_path" -type d 2>/dev/null | wc -l)
# Count the total number of files
total_files=$(find "$directory_path" -type f 2>/dev/null | wc -l)
# Initialize a counter for directories and files
dir_count=0
file_count=0
# Function to print progress
print_progress() {
echo -ne "\rDirectories: $dir_count/$total_dirs Files: $file_count/$total_files"
}
# Check if the script is running within a Docker container # Check if the script is running within a Docker container
if [ -f "/.dockerenv" ]; then if [ -f "/.dockerenv" ]; then
echo "Script is running within a Docker container. Exiting with error." echo "Script is running within a Docker container. Exiting with error."
exit 1 # Exit with an error code if running in Docker exit 1 # Exit with an error code if running in Docker
else else
echo "Script is not running within a Docker container. Executing permissions changes..." 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) # Run the commands to set permissions for directories
sudo chmod 644 $(find "$directory_path" -type f) echo "Changing permissions for directories:"
for dir in $(find "$directory_path" -type d 2>/dev/null); do
if [ -e "$dir" ]; then
sudo chmod 700 "$dir" && ((dir_count++))
fi fi
print_progress
done
# Run the commands to set permissions for files
echo -e "\nChanging permissions for files:"
for file in $(find "$directory_path" -type f 2>/dev/null); do
if [ -e "$file" ]; then
sudo chmod 644 "$file" && ((file_count++))
fi
print_progress
done
echo "You will now need to execute a chmod +x on all bedrock executables"
fi
echo "" # Adding a new line after the loop for better readability

View File

@ -1,4 +1,22 @@
# Changelog # Changelog
## --- [4.3.2] - 2024/04/07
### Refactor
- Refactor ServerJars caching and move to api.serverjars.com ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/744) | [Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/746))
### Bug fixes
- Fix migrator issue when jumping versions ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/734))
- Fix backend issue causing error when restoring backups in 4.3.x ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/736))
- Fix backend issue causing error when cloning servers in 4.3.x ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/741))
- Bump orjson for CVE-2024-27454 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/747))
- Fix calling of orjson JSONDecodeError class ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/747))
- Fix stack on Crafty permissions route request in API ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/745))
### Tweaks
- Clean up remaining http handler references ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/733))
- Remove version disclosure on login page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/737))
- Add openjdk-21 for recent versions of MC ([Commit](https://gitlab.com/crafty-controller/crafty-4/-/commit/77b0c2c9d2eac124a7504a3d3916fa22d29fa9d1))
### Lang
- Update `it_IT, cs_CS` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/739) | [Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/742))
<br><br>
## --- [4.3.1] - 2024/03/18 ## --- [4.3.1] - 2024/03/18
### Bug fixes ### Bug fixes
- Fix Server ID Rework for backups, schedules, and roles (INT ID to UUID migration) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/729)) - Fix Server ID Rework for backups, schedules, and roles (INT ID to UUID migration) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/729))

View File

@ -25,6 +25,7 @@ RUN apt-get update \
openjdk-8-jre-headless \ openjdk-8-jre-headless \
openjdk-11-jre-headless \ openjdk-11-jre-headless \
openjdk-17-jre-headless \ openjdk-17-jre-headless \
openjdk-21-jre-headless \
tzdata \ tzdata \
&& apt-get autoremove \ && apt-get autoremove \
&& apt-get clean && apt-get clean

View File

@ -1,5 +1,5 @@
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com) [![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
# Crafty Controller 4.3.1 # Crafty Controller 4.3.2
> Python based Control Panel for your Minecraft Server > Python based Control Panel for your Minecraft Server
## What is Crafty Controller? ## What is Crafty Controller?

View File

@ -47,7 +47,7 @@ class ServerPermsController:
new_server_id, new_server_id,
role.role_id, role.role_id,
PermissionsServers.get_permissions_mask( PermissionsServers.get_permissions_mask(
int(role.role_id), int(old_server_id) int(role.role_id), old_server_id
), ),
) )
# Permissions_Servers.add_role_server( # Permissions_Servers.add_role_server(

View File

@ -12,13 +12,15 @@ from app.classes.shared.file_helpers import FileHelpers
from app.classes.shared.websocket_manager import WebSocketManager from app.classes.shared.websocket_manager import WebSocketManager
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Temp type var until sjars restores generic fetchTypes0
SERVERJARS_TYPES = ["modded", "proxies", "servers", "vanilla"]
PAPERJARS = ["paper", "folia"] PAPERJARS = ["paper", "folia"]
class ServerJars: class ServerJars:
def __init__(self, helper): def __init__(self, helper):
self.helper = helper self.helper = helper
self.base_url = "https://serverjars.com" self.base_url = "https://api.serverjars.com"
self.paper_base = "https://api.papermc.io" self.paper_base = "https://api.papermc.io"
@staticmethod @staticmethod
@ -82,6 +84,183 @@ class ServerJars:
builds = api_data.get("builds", []) builds = api_data.get("builds", [])
return builds[-1] if builds else None return builds[-1] if builds else None
def _read_cache(self):
cache_file = self.helper.serverjar_cache
cache = {}
try:
with open(cache_file, "r", encoding="utf-8") as f:
cache = json.load(f)
except Exception as e:
logger.error(f"Unable to read serverjars.com cache file: {e}")
return cache
def get_serverjar_data(self):
data = self._read_cache()
return data.get("types")
def _check_sjars_api_alive(self):
logger.info("Checking serverjars.com API status")
check_url = f"{self.base_url}"
try:
response = requests.get(check_url, timeout=2)
response_json = response.json()
if (
response.status_code in [200, 201]
and response_json.get("status") == "success"
and response_json.get("response", {}).get("status") == "ok"
):
logger.info("Serverjars.com API is alive and responding as expected")
return True
except Exception as e:
logger.error(f"Unable to connect to serverjar.com API due to error: {e}")
return False
logger.error(
"Serverjars.com API is not responding as expected or unable to contact"
)
return False
def _fetch_projects_for_type(self, server_type):
"""
Fetches projects for a given server type from the ServerJars API.
"""
try:
response = requests.get(
f"{self.base_url}/api/fetchTypes/{server_type}", timeout=5
)
response.raise_for_status() # Ensure HTTP errors are caught
data = response.json()
if data.get("status") == "success":
return data["response"].get("servers", [])
except requests.RequestException as e:
print(f"Error fetching projects for type {server_type}: {e}")
return []
def _get_server_type_list(self):
"""
Builds the type structure with projects fetched for each type.
"""
type_structure = {}
for server_type in SERVERJARS_TYPES:
projects = self._fetch_projects_for_type(server_type)
type_structure[server_type] = {project: [] for project in projects}
return type_structure
def _get_jar_versions(self, server_type, project_name, max_ver=50):
"""
Grabs available versions for specified project
Args:
server_type (str): Server Type Category (modded, servers, etc)
project_name (str): Target project (paper, forge, magma, etc)
max (int, optional): Max versions returned. Defaults to 50.
Returns:
list: An array of versions
"""
url = f"{self.base_url}/api/fetchAll/{server_type}/{project_name}?max={max_ver}"
try:
response = requests.get(url, timeout=5)
response.raise_for_status() # Ensure HTTP errors are caught
data = response.json()
logger.debug(f"Received data for {server_type}/{project_name}: {data}")
if data.get("status") == "success":
versions = [
item.get("version")
for item in data.get("response", [])
if "version" in item
]
versions.reverse() # Reverse so versions are newest -> oldest
logger.debug(f"Versions extracted: {versions}")
return versions
except requests.RequestException as e:
logger.error(
f"Error fetching jar versions for {server_type}/{project_name}: {e}"
)
return []
def _refresh_cache(self):
"""
Contains the shared logic for refreshing the cache.
This method is called by both manual_refresh_cache and refresh_cache methods.
"""
now = datetime.now()
cache_data = {
"last_refreshed": now.strftime("%m/%d/%Y, %H:%M:%S"),
"types": self._get_server_type_list(),
}
for server_type, projects in cache_data["types"].items():
for project_name in projects:
versions = self._get_jar_versions(server_type, project_name)
cache_data["types"][server_type][project_name] = versions
for paper_project in PAPERJARS:
cache_data["types"]["servers"][paper_project] = self.get_paper_versions(
paper_project
)
return cache_data
def manual_refresh_cache(self):
"""
Manually triggers the cache refresh process.
"""
if not self._check_sjars_api_alive():
logger.error("ServerJars API is not available.")
return False
logger.info("Manual cache refresh requested.")
cache_data = self._refresh_cache()
# Save the updated cache data
try:
with open(self.helper.serverjar_cache, "w", encoding="utf-8") as cache_file:
json.dump(cache_data, cache_file, indent=4)
logger.info("Cache file successfully refreshed manually.")
except Exception as e:
logger.error(f"Failed to update cache file manually: {e}")
def refresh_cache(self):
"""
Automatically trigger cache refresh process based age.
This method checks if the cache file is older than a specified number of days
before deciding to refresh.
"""
cache_file_path = self.helper.serverjar_cache
# Determine if the cache is old and needs refreshing
cache_old = self.helper.is_file_older_than_x_days(cache_file_path)
# debug override
# cache_old = True
if not self._check_sjars_api_alive():
logger.error("ServerJars API is not available.")
return False
if not cache_old:
logger.info("Cache file is not old enough to require automatic refresh.")
return False
logger.info("Automatic cache refresh initiated due to old cache.")
cache_data = self._refresh_cache()
# Save the updated cache data
try:
with open(cache_file_path, "w", encoding="utf-8") as cache_file:
json.dump(cache_data, cache_file, indent=4)
logger.info("Cache file successfully refreshed automatically.")
except Exception as e:
logger.error(f"Failed to update cache file automatically: {e}")
def get_fetch_url(self, jar, server, version): def get_fetch_url(self, jar, server, version):
""" """
Constructs the URL for downloading a server JAR file based on the server type. Constructs the URL for downloading a server JAR file based on the server type.
@ -132,151 +311,6 @@ class ServerJars:
logger.error(f"An error occurred while constructing fetch URL: {e}") logger.error(f"An error occurred while constructing fetch URL: {e}")
return None return None
def _get_api_result(self, call_url: str):
full_url = f"{self.base_url}{call_url}"
try:
response = requests.get(full_url, timeout=2)
response.raise_for_status()
api_data = json.loads(response.content)
except Exception as e:
logger.error(f"Unable to load {full_url} api due to error: {e}")
return {}
api_result = api_data.get("status")
api_response = api_data.get("response", {})
if api_result != "success":
logger.error(f"Api returned a failed status: {api_result}")
return {}
return api_response
def _read_cache(self):
cache_file = self.helper.serverjar_cache
cache = {}
try:
with open(cache_file, "r", encoding="utf-8") as f:
cache = json.load(f)
except Exception as e:
logger.error(f"Unable to read serverjars.com cache file: {e}")
return cache
def get_serverjar_data(self):
data = self._read_cache()
return data.get("types")
def _check_api_alive(self):
logger.info("Checking serverjars.com API status")
check_url = f"{self.base_url}/api/fetchTypes"
try:
response = requests.get(check_url, timeout=2)
if response.status_code in [200, 201]:
logger.info("Serverjars.com API is alive")
return True
except Exception as e:
logger.error(f"Unable to connect to serverjar.com api due to error: {e}")
return {}
logger.error("unable to contact serverjars.com api")
return False
def manual_refresh_cache(self):
cache_file = self.helper.serverjar_cache
# debug override
# cache_old = True
# if the API is down... we bomb out
if not self._check_api_alive():
return False
logger.info("Manual Refresh requested.")
now = datetime.now()
data = {
"last_refreshed": now.strftime("%m/%d/%Y, %H:%M:%S"),
"types": {},
}
jar_types = self._get_server_type_list()
data["types"].update(jar_types)
for s in data["types"]:
data["types"].update({s: dict.fromkeys(data["types"].get(s), {})})
for j in data["types"].get(s):
versions = self._get_jar_details(j, s)
data["types"][s].update({j: versions})
for item in PAPERJARS:
data["types"]["servers"][item] = self.get_paper_versions(item)
# save our cache
try:
with open(cache_file, "w", encoding="utf-8") as f:
f.write(json.dumps(data, indent=4))
logger.info("Cache file refreshed")
except Exception as e:
logger.error(f"Unable to update serverjars.com cache file: {e}")
def refresh_cache(self):
cache_file = self.helper.serverjar_cache
cache_old = self.helper.is_file_older_than_x_days(cache_file)
# debug override
# cache_old = True
# if the API is down... we bomb out
if not self._check_api_alive():
return False
logger.info("Checking Cache file age")
# if file is older than 1 day
if cache_old:
logger.info("Cache file is over 1 day old, refreshing")
now = datetime.now()
data = {
"last_refreshed": now.strftime("%m/%d/%Y, %H:%M:%S"),
"types": {},
}
jar_types = self._get_server_type_list()
data["types"].update(jar_types)
for s in data["types"]:
data["types"].update({s: dict.fromkeys(data["types"].get(s), {})})
for j in data["types"].get(s):
versions = self._get_jar_details(j, s)
data["types"][s].update({j: versions})
for item in PAPERJARS:
data["types"]["servers"][item] = self.get_paper_versions(item)
# save our cache
try:
with open(cache_file, "w", encoding="utf-8") as f:
f.write(json.dumps(data, indent=4))
logger.info("Cache file refreshed")
except Exception as e:
logger.error(f"Unable to update serverjars.com cache file: {e}")
def _get_jar_details(self, server_type, jar_type="servers"):
url = f"/api/fetchAll/{jar_type}/{server_type}"
response = self._get_api_result(url)
temp = []
for v in response:
temp.append(v.get("version"))
time.sleep(0.5)
return temp
def _get_server_type_list(self):
url = "/api/fetchTypes/"
response = self._get_api_result(url)
if "bedrock" in response.keys():
# remove pocketmine from options
del response["bedrock"]
return response
def download_jar(self, jar, server, version, path, server_id): def download_jar(self, jar, server, version, path, server_id):
update_thread = threading.Thread( update_thread = threading.Thread(
name=f"server_download-{server_id}-{server}-{version}", name=f"server_download-{server_id}-{server}-{version}",

View File

@ -575,7 +575,7 @@ class Controller:
): ):
server_obj = self.servers.get_server_obj(new_server_id) server_obj = self.servers.get_server_obj(new_server_id)
url = ( url = (
"https://serverjars.com/api/fetchJar/" "https://api.serverjars.com/api/fetchJar/"
f"{create_data['category']}" f"{create_data['category']}"
f"/{create_data['type']}/{create_data['version']}" f"/{create_data['type']}/{create_data['version']}"
) )
@ -1131,7 +1131,7 @@ class Controller:
server_obj.path = new_local_server_path server_obj.path = new_local_server_path
failed = False failed = False
for s in self.servers.failed_servers: for s in self.servers.failed_servers:
if int(s["server_id"]) == int(server.get("server_id")): if s["server_id"] == server.get("server_id"):
failed = True failed = True
if not failed: if not failed:
self.servers.update_server(server_obj) self.servers.update_server(server_obj)

View File

@ -372,11 +372,11 @@ class MigrationManager(object):
Create migrator Create migrator
""" """
migrator = Migrator(self.database) migrator = Migrator(self.database)
# Removing the up_one to prevent running all # Running false migrations to retrives the schemes of
# migrations each time we got a new one. # the precedents created tables in the table_dict element
# It's handled by migration.up() function. # It's useful to run the new migrations
# for name in self.done: for name in self.done:
# self.up_one(name, migrator, True) self.up_one(name, migrator, True)
return migrator return migrator
def compile(self, name, migrate="", rollback=""): def compile(self, name, migrate="", rollback=""):

View File

@ -1403,7 +1403,7 @@ class PanelHandler(BaseHandler):
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
exec_user["user_id"], exec_user["user_id"],
f"Removed user {target_user['username']} (UID:{user_id})", f"Removed user {target_user['username']} (UID:{user_id})",
server_id=0, server_id=None,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )
self.redirect("/panel/panel_config") self.redirect("/panel/panel_config")

View File

@ -228,7 +228,7 @@ class PublicHandler(BaseHandler):
) )
# log this login # log this login
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
user_data.user_id, "Logged in", 0, self.get_remote_ip() user_data.user_id, "Logged in", None, self.get_remote_ip()
) )
return self.finish_json( return self.finish_json(
@ -254,7 +254,7 @@ class PublicHandler(BaseHandler):
) )
# log this failed login attempt # log this failed login attempt
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
user_data.user_id, "Tried to log in", 0, self.get_remote_ip() user_data.user_id, "Tried to log in", None, self.get_remote_ip()
) )
return self.finish_json( return self.finish_json(
403, 403,

View File

@ -101,7 +101,7 @@ class ApiAuthLoginHandler(BaseApiHandler):
# log this login # log this login
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
user_data.user_id, "logged in via the API", 0, self.get_remote_ip() user_data.user_id, "logged in via the API", None, self.get_remote_ip()
) )
self.finish_json( self.finish_json(
@ -119,7 +119,7 @@ class ApiAuthLoginHandler(BaseApiHandler):
else: else:
# log this failed login attempt # log this failed login attempt
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
user_data.user_id, "Tried to log in", 0, self.get_remote_ip() user_data.user_id, "Tried to log in", None, self.get_remote_ip()
) )
self.finish_json( self.finish_json(
401, 401,

View File

@ -9,7 +9,6 @@ from app.classes.web.base_api_handler import BaseApiHandler
config_json_schema = { config_json_schema = {
"type": "object", "type": "object",
"properties": { "properties": {
"http_port": {"type": "integer"},
"https_port": {"type": "integer"}, "https_port": {"type": "integer"},
"language": { "language": {
"type": "string", "type": "string",
@ -107,7 +106,7 @@ class ApiCraftyConfigIndexHandler(BaseApiHandler):
try: try:
data = orjson.loads(self.request.body) data = orjson.loads(self.request.body)
except orjson.decoder.JSONDecodeError as e: except orjson.JSONDecodeError as e:
return self.finish_json( return self.finish_json(
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
) )
@ -129,7 +128,7 @@ class ApiCraftyConfigIndexHandler(BaseApiHandler):
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
user["user_id"], user["user_id"],
"edited config.json", "edited config.json",
server_id=0, server_id=None,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )
@ -188,7 +187,7 @@ class ApiCraftyCustomizeIndexHandler(BaseApiHandler):
try: try:
data = orjson.loads(self.request.body) data = orjson.loads(self.request.body)
except orjson.decoder.JSONDecodeError as e: except orjson.JSONDecodeError as e:
return self.finish_json( return self.finish_json(
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
) )
@ -226,7 +225,7 @@ class ApiCraftyCustomizeIndexHandler(BaseApiHandler):
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
user["user_id"], user["user_id"],
f"customized login photo: {data['photo']}/{data['opacity']}", f"customized login photo: {data['photo']}/{data['opacity']}",
server_id=0, server_id=None,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )
self.controller.management.set_login_opacity(int(data["opacity"])) self.controller.management.set_login_opacity(int(data["opacity"]))

View File

@ -68,7 +68,7 @@ class ApiCraftyConfigServerDirHandler(BaseApiHandler):
try: try:
data = orjson.loads(self.request.body) data = orjson.loads(self.request.body)
except orjson.decoder.JSONDecodeError as e: except orjson.JSONDecodeError as e:
return self.finish_json( return self.finish_json(
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
) )
@ -109,7 +109,7 @@ class ApiCraftyConfigServerDirHandler(BaseApiHandler):
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
auth_data[4]["user_id"], auth_data[4]["user_id"],
f"updated master servers dir to {new_dir}/servers", f"updated master servers dir to {new_dir}/servers",
server_id=0, server_id=None,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )

View File

@ -161,7 +161,7 @@ class ApiRolesIndexHandler(BaseApiHandler):
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
user["user_id"], user["user_id"],
f"created role {role_name} (RID:{role_id})", f"created role {role_name} (RID:{role_id})",
server_id=0, server_id=None,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )

View File

@ -112,7 +112,7 @@ class ApiRolesRoleIndexHandler(BaseApiHandler):
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
user["user_id"], user["user_id"],
f"deleted role with ID {role_id}", f"deleted role with ID {role_id}",
server_id=0, server_id=None,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )
@ -133,7 +133,7 @@ class ApiRolesRoleIndexHandler(BaseApiHandler):
try: try:
data = orjson.loads(self.request.body) data = orjson.loads(self.request.body)
except orjson.decoder.JSONDecodeError as e: except orjson.JSONDecodeError as e:
return self.finish_json( return self.finish_json(
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
) )
@ -172,7 +172,7 @@ class ApiRolesRoleIndexHandler(BaseApiHandler):
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
user["user_id"], user["user_id"],
f"modified role with ID {role_id}", f"modified role with ID {role_id}",
server_id=0, server_id=None,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )

View File

@ -33,6 +33,17 @@ class ApiServersServerActionHandler(BaseApiHandler):
self.controller.crafty_perms.can_create_server(auth_data[4]["user_id"]) self.controller.crafty_perms.can_create_server(auth_data[4]["user_id"])
or auth_data[4]["superuser"] or auth_data[4]["superuser"]
): ):
srv_object = self.controller.servers.get_server_instance_by_id(
server_id
)
if srv_object.check_running():
return self.finish_json(
409,
{
"status": "error",
"error": "Server Running!",
},
)
self._clone_server(server_id, auth_data[4]["user_id"]) self._clone_server(server_id, auth_data[4]["user_id"])
return self.finish_json(200, {"status": "ok"}) return self.finish_json(200, {"status": "ok"})
return self.finish_json( return self.finish_json(
@ -67,20 +78,29 @@ class ApiServersServerActionHandler(BaseApiHandler):
name_counter += 1 name_counter += 1
new_server_name = server_data.get("server_name") + f" (Copy {name_counter})" new_server_name = server_data.get("server_name") + f" (Copy {name_counter})"
new_server_id = self.controller.servers.create_server( new_server_id = self.helper.create_uuid()
new_server_name, new_server_path = os.path.join(self.helper.servers_dir, new_server_id)
None, new_backup_path = os.path.join(self.helper.backup_path, new_server_id)
"", new_server_command = str(server_data.get("execution_command")).replace(
None, server_id, new_server_id
server_data.get("executable"), )
None, new_server_log_path = server_data.get("log_path").replace(
server_data.get("stop_command"), server_id, new_server_id
server_data.get("type"),
user_id,
server_data.get("server_port"),
) )
new_server_path = os.path.join(self.helper.servers_dir, new_server_id) self.controller.register_server(
new_server_name,
new_server_id,
new_server_path,
new_backup_path,
new_server_command,
server_data.get("executable"),
new_server_log_path,
server_data.get("stop_command"),
server_data.get("server_port"),
user_id,
server_data.get("type"),
)
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
user_id, user_id,
@ -92,18 +112,6 @@ class ApiServersServerActionHandler(BaseApiHandler):
# copy the old server # copy the old server
FileHelpers.copy_dir(server_data.get("path"), new_server_path) FileHelpers.copy_dir(server_data.get("path"), new_server_path)
# TODO get old server DB data to individual variables
new_server_command = str(server_data.get("execution_command"))
new_server_log_file = str(
self.helper.get_os_understandable_path(server_data.get("log_path"))
)
server: Servers = self.controller.servers.get_server_obj(new_server_id)
server.path = new_server_path
server.log_path = new_server_log_file
server.execution_command = new_server_command
self.controller.servers.update_server(server)
for role in self.controller.server_perms.get_server_roles(server_id): for role in self.controller.server_perms.get_server_roles(server_id):
mask = self.controller.server_perms.get_permissions_mask( mask = self.controller.server_perms.get_permissions_mask(
role.role_id, server_id role.role_id, server_id

View File

@ -203,7 +203,7 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler):
except JobLookupError as e: except JobLookupError as e:
logger.info("No active tasks found for server: {e}") logger.info("No active tasks found for server: {e}")
self.controller.remove_server(server_id, True) self.controller.remove_server(server_id, True)
except Exception as e: except (FileNotFoundError, NotADirectoryError) as e:
return self.finish_json( return self.finish_json(
400, {"status": "error", "error": f"NO BACKUP FOUND {e}"} 400, {"status": "error", "error": f"NO BACKUP FOUND {e}"}
) )

View File

@ -177,7 +177,7 @@ class ApiUsersIndexHandler(BaseApiHandler):
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
user["user_id"], user["user_id"],
f"added user {username} (UID:{user_id}) with roles {roles}", f"added user {username} (UID:{user_id}) with roles {roles}",
server_id=0, server_id=None,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )

View File

@ -43,7 +43,7 @@ class ApiUsersUserKeyHandler(BaseApiHandler):
auth_data[4]["user_id"], auth_data[4]["user_id"],
f"Generated a new API token for the key {key.name} " f"Generated a new API token for the key {key.name} "
f"from user with UID: {key.user_id}", f"from user with UID: {key.user_id}",
server_id=0, server_id=None,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )
data_key = self.controller.authentication.generate( data_key = self.controller.authentication.generate(
@ -173,7 +173,7 @@ class ApiUsersUserKeyHandler(BaseApiHandler):
f"Added API key {data['name']} with crafty permissions " f"Added API key {data['name']} with crafty permissions "
f"{data['crafty_permissions_mask']}" f"{data['crafty_permissions_mask']}"
f" and {data['server_permissions_mask']} for user with UID: {user_id}", f" and {data['server_permissions_mask']} for user with UID: {user_id}",
server_id=0, server_id=None,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )
self.finish_json(200, {"status": "ok", "data": {"id": key_id}}) self.finish_json(200, {"status": "ok", "data": {"id": key_id}})
@ -233,7 +233,7 @@ class ApiUsersUserKeyHandler(BaseApiHandler):
auth_data[4]["user_id"], auth_data[4]["user_id"],
f"Removed API key {target_key} " f"Removed API key {target_key} "
f"(ID: {key_id}) from user {auth_data[4]['user_id']}", f"(ID: {key_id}) from user {auth_data[4]['user_id']}",
server_id=0, server_id=None,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )

View File

@ -94,7 +94,7 @@ class ApiUsersUserIndexHandler(BaseApiHandler):
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
user["user_id"], user["user_id"],
f"deleted the user {user_id}", f"deleted the user {user_id}",
server_id=0, server_id=None,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )
@ -283,7 +283,7 @@ class ApiUsersUserIndexHandler(BaseApiHandler):
f"edited user {user_obj.username} (UID: {user_id})" f"edited user {user_obj.username} (UID: {user_id})"
f"with roles {user_obj.roles}" f"with roles {user_obj.roles}"
), ),
server_id=0, server_id=None,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )

View File

@ -52,6 +52,8 @@ class ApiUsersUserPermissionsHandler(BaseApiHandler):
}, },
) )
counter_data = PermissionsCrafty.get_created_quantity_list(user_id)
self.finish_json( self.finish_json(
200, 200,
{ {
@ -59,9 +61,9 @@ class ApiUsersUserPermissionsHandler(BaseApiHandler):
"data": { "data": {
"permissions": res_data.permissions, "permissions": res_data.permissions,
"counters": { "counters": {
SERVER_CREATION: res_data.created_server, SERVER_CREATION: counter_data["SERVER_CREATION"],
USER_CONFIG: res_data.created_user, USER_CONFIG: counter_data["USER_CONFIG"],
ROLES_CONFIG: res_data.created_role, ROLES_CONFIG: counter_data["ROLES_CONFIG"],
}, },
"limits": { "limits": {
SERVER_CREATION: res_data.limit_server_creation, SERVER_CREATION: res_data.limit_server_creation,

View File

@ -147,7 +147,7 @@ class ServerHandler(BaseHandler):
page_data["server_api"] = False page_data["server_api"] = False
if page_data["online"]: if page_data["online"]:
page_data["server_api"] = self.helper.check_address_status( page_data["server_api"] = self.helper.check_address_status(
"https://serverjars.com/api/fetchTypes" "https://api.serverjars.com"
) )
page_data["server_types"] = self.controller.server_jars.get_serverjar_data() page_data["server_types"] = self.controller.server_jars.get_serverjar_data()
page_data["js_server_types"] = json.dumps( page_data["js_server_types"] = json.dumps(

View File

@ -98,7 +98,6 @@ class Webserver:
# let's verify we have an SSL cert # let's verify we have an SSL cert
self.helper.create_self_signed_cert() self.helper.create_self_signed_cert()
http_port = self.helper.get_setting("http_port")
https_port = self.helper.get_setting("https_port") https_port = self.helper.get_setting("https_port")
debug_errors = self.helper.get_setting("show_errors") debug_errors = self.helper.get_setting("show_errors")
@ -110,9 +109,6 @@ class Webserver:
cookie_secret = self.helper.random_string_generator(32) cookie_secret = self.helper.random_string_generator(32)
HelpersManagement.set_cookie_secret(cookie_secret) HelpersManagement.set_cookie_secret(cookie_secret)
if not http_port and http_port != 0:
http_port = 8000
if not https_port: if not https_port:
https_port = 8443 https_port = 8443
@ -125,7 +121,7 @@ class Webserver:
), ),
} }
logger.info(f"Starting Web Server on ports http:{http_port} https:{https_port}") logger.info(f"Starting Web Server on ports https:{https_port}")
asyncio.set_event_loop(asyncio.new_event_loop()) asyncio.set_event_loop(asyncio.new_event_loop())

View File

@ -55,7 +55,7 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler):
self.controller.management.add_to_audit_log_raw( self.controller.management.add_to_audit_log_raw(
"unknown", "unknown",
0, 0,
0, None,
"Someone tried to connect via WebSocket without proper authentication", "Someone tried to connect via WebSocket without proper authentication",
self.get_remote_ip(), self.get_remote_ip(),
) )

View File

@ -1,5 +1,5 @@
{ {
"major": 4, "major": 4,
"minor": 3, "minor": 3,
"sub": 1 "sub": 2
} }

View File

@ -111,8 +111,7 @@
</div> </div>
<div class="text-block text-center my-3"> <div class="text-block text-center my-3">
<span class="text-small font-weight-semibold"><a href="https://craftycontrol.com/">Crafty Control <span class="text-small font-weight-semibold"><a href="https://craftycontrol.com/">Crafty Control</a> </span>
{{data['version'] }}</a> </span>
</div> </div>
<div class="text-block text-center my-3"> <div class="text-block text-center my-3">

View File

@ -54,9 +54,6 @@ def migrate(migrator: Migrator, database, **kwargs):
database = db database = db
try: try:
logger.info("Migrating Data from Int to UUID (Type Change)")
Console.info("Migrating Data from Int to UUID (Type Change)")
# Changes on Server Table # Changes on Server Table
migrator.alter_column_type( migrator.alter_column_type(
Servers, Servers,
@ -87,11 +84,6 @@ def migrate(migrator: Migrator, database, **kwargs):
), ),
) )
migrator.run()
logger.info("Migrating Data from Int to UUID (Type Change) : SUCCESS")
Console.info("Migrating Data from Int to UUID (Type Change) : SUCCESS")
except Exception as ex: except Exception as ex:
logger.error("Error while migrating Data from Int to UUID (Type Change)") logger.error("Error while migrating Data from Int to UUID (Type Change)")
logger.error(ex) logger.error(ex)
@ -101,118 +93,6 @@ def migrate(migrator: Migrator, database, **kwargs):
last_migration.delete() last_migration.delete()
return return
try:
logger.info("Migrating Data from Int to UUID (Foreign Keys)")
Console.info("Migrating Data from Int to UUID (Foreign Keys)")
# Changes on Audit Log Table
for audit_log in AuditLog.select():
old_server_id = audit_log.server_id_id
if old_server_id == "0" or old_server_id is None:
server_uuid = None
else:
try:
server = Servers.get_by_id(old_server_id)
server_uuid = server.server_uuid
except:
server_uuid = old_server_id
AuditLog.update(server_id=server_uuid).where(
AuditLog.audit_id == audit_log.audit_id
).execute()
# Changes on Webhooks Log Table
for webhook in Webhooks.select():
old_server_id = webhook.server_id_id
try:
server = Servers.get_by_id(old_server_id)
server_uuid = server.server_uuid
except:
server_uuid = old_server_id
Webhooks.update(server_id=server_uuid).where(
Webhooks.id == webhook.id
).execute()
# Changes on Schedules Log Table
for schedule in Schedules.select():
old_server_id = schedule.server_id_id
try:
server = Servers.get_by_id(old_server_id)
server_uuid = server.server_uuid
except:
server_uuid = old_server_id
Schedules.update(server_id=server_uuid).where(
Schedules.schedule_id == schedule.schedule_id
).execute()
# Changes on Backups Log Table
for backup in Backups.select():
old_server_id = backup.server_id_id
try:
server = Servers.get_by_id(old_server_id)
server_uuid = server.server_uuid
except:
server_uuid = old_server_id
Backups.update(server_id=server_uuid).where(
Backups.server_id == old_server_id
).execute()
# Changes on RoleServers Log Table
for role_servers in RoleServers.select():
old_server_id = role_servers.server_id_id
try:
server = Servers.get_by_id(old_server_id)
server_uuid = server.server_uuid
except:
server_uuid = old_server_id
RoleServers.update(server_id=server_uuid).where(
RoleServers.role_id == role_servers.id
and RoleServers.server_id == old_server_id
).execute()
logger.info("Migrating Data from Int to UUID (Foreign Keys) : SUCCESS")
Console.info("Migrating Data from Int to UUID (Foreign Keys) : SUCCESS")
except Exception as ex:
logger.error("Error while migrating Data from Int to UUID (Foreign Keys)")
logger.error(ex)
Console.error("Error while migrating Data from Int to UUID (Foreign Keys)")
Console.error(ex)
last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count())
last_migration.delete()
return
try:
logger.info("Migrating Data from Int to UUID (Primary Keys)")
Console.info("Migrating Data from Int to UUID (Primary Keys)")
# Migrating servers from the old id type to the new one
for server in Servers.select():
Servers.update(server_id=server.server_uuid).where(
Servers.server_id == server.server_id
).execute()
logger.info("Migrating Data from Int to UUID (Primary Keys) : SUCCESS")
Console.info("Migrating Data from Int to UUID (Primary Keys) : SUCCESS")
except Exception as ex:
logger.error("Error while migrating Data from Int to UUID (Primary Keys)")
logger.error(ex)
Console.error("Error while migrating Data from Int to UUID (Primary Keys)")
Console.error(ex)
last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count())
last_migration.delete()
return
# Changes on Server Table
logger.info("Migrating Data from Int to UUID (Removing UUID Field from Servers)")
Console.info("Migrating Data from Int to UUID (Removing UUID Field from Servers)")
migrator.drop_columns("servers", ["server_uuid"])
migrator.run()
logger.info(
"Migrating Data from Int to UUID (Removing UUID Field from Servers) : SUCCESS"
)
Console.info(
"Migrating Data from Int to UUID (Removing UUID Field from Servers) : SUCCESS"
)
return return

View File

@ -0,0 +1,326 @@
import datetime
import uuid
import peewee
import logging
from app.classes.shared.console import Console
from app.classes.shared.migration import Migrator, MigrateHistory
from app.classes.models.management import (
AuditLog,
Webhooks,
Schedules,
Backups,
)
from app.classes.models.server_permissions import RoleServers
from app.classes.models.base_model import BaseModel
logger = logging.getLogger(__name__)
def migrate(migrator: Migrator, database, **kwargs):
"""
Write your migrations here.
"""
db = database
# **********************************************************************************
# Servers New Model from Old (easier to migrate without dunmping Database)
# **********************************************************************************
class Servers(peewee.Model):
server_id = peewee.CharField(primary_key=True, default=str(uuid.uuid4()))
created = peewee.DateTimeField(default=datetime.datetime.now)
server_uuid = peewee.CharField(default="", index=True)
server_name = peewee.CharField(default="Server", index=True)
path = peewee.CharField(default="")
backup_path = peewee.CharField(default="")
executable = peewee.CharField(default="")
log_path = peewee.CharField(default="")
execution_command = peewee.CharField(default="")
auto_start = peewee.BooleanField(default=0)
auto_start_delay = peewee.IntegerField(default=10)
crash_detection = peewee.BooleanField(default=0)
stop_command = peewee.CharField(default="stop")
executable_update_url = peewee.CharField(default="")
server_ip = peewee.CharField(default="127.0.0.1")
server_port = peewee.IntegerField(default=25565)
logs_delete_after = peewee.IntegerField(default=0)
type = peewee.CharField(default="minecraft-java")
show_status = peewee.BooleanField(default=1)
created_by = peewee.IntegerField(default=-100)
shutdown_timeout = peewee.IntegerField(default=60)
ignored_exits = peewee.CharField(default="0")
class Meta:
table_name = "servers"
database = db
this_migration = MigrateHistory.get_or_none(
MigrateHistory.name == "20240217_rework_servers_uuid_part2"
)
if this_migration is not None:
Console.debug("Update database already done, skipping this part")
return
else:
servers_columns = db.get_columns("servers")
if not any(
column_data.name == "server_uuid" for column_data in servers_columns
):
Console.debug(
"Servers.server_uuid already deleted in Crafty version 4.3.0, skipping this part"
)
return
try:
logger.info("Migrating Data from Int to UUID (Foreign Keys)")
Console.info("Migrating Data from Int to UUID (Foreign Keys)")
# Changes on Audit Log Table
for audit_log in AuditLog.select():
old_server_id = audit_log.server_id_id
if old_server_id == "0" or old_server_id is None:
server_uuid = None
else:
try:
server = Servers.get_by_id(old_server_id)
server_uuid = server.server_uuid
except:
server_uuid = old_server_id
AuditLog.update(server_id=server_uuid).where(
AuditLog.audit_id == audit_log.audit_id
).execute()
# Changes on Webhooks Log Table
for webhook in Webhooks.select():
old_server_id = webhook.server_id_id
try:
server = Servers.get_by_id(old_server_id)
server_uuid = server.server_uuid
except:
server_uuid = old_server_id
Webhooks.update(server_id=server_uuid).where(
Webhooks.id == webhook.id
).execute()
# Changes on Schedules Log Table
for schedule in Schedules.select():
old_server_id = schedule.server_id_id
try:
server = Servers.get_by_id(old_server_id)
server_uuid = server.server_uuid
except:
server_uuid = old_server_id
Schedules.update(server_id=server_uuid).where(
Schedules.schedule_id == schedule.schedule_id
).execute()
# Changes on Backups Log Table
for backup in Backups.select():
old_server_id = backup.server_id_id
try:
server = Servers.get_by_id(old_server_id)
server_uuid = server.server_uuid
except:
server_uuid = old_server_id
Backups.update(server_id=server_uuid).where(
Backups.server_id == old_server_id
).execute()
# Changes on RoleServers Log Table
for role_servers in RoleServers.select():
old_server_id = role_servers.server_id_id
try:
server = Servers.get_by_id(old_server_id)
server_uuid = server.server_uuid
except:
server_uuid = old_server_id
RoleServers.update(server_id=server_uuid).where(
RoleServers.role_id == role_servers.id
and RoleServers.server_id == old_server_id
).execute()
logger.info("Migrating Data from Int to UUID (Foreign Keys) : SUCCESS")
Console.info("Migrating Data from Int to UUID (Foreign Keys) : SUCCESS")
except Exception as ex:
logger.error("Error while migrating Data from Int to UUID (Foreign Keys)")
logger.error(ex)
Console.error("Error while migrating Data from Int to UUID (Foreign Keys)")
Console.error(ex)
last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count())
last_migration.delete()
return
try:
logger.info("Migrating Data from Int to UUID (Primary Keys)")
Console.info("Migrating Data from Int to UUID (Primary Keys)")
# Migrating servers from the old id type to the new one
for server in Servers.select():
Servers.update(server_id=server.server_uuid).where(
Servers.server_id == server.server_id
).execute()
logger.info("Migrating Data from Int to UUID (Primary Keys) : SUCCESS")
Console.info("Migrating Data from Int to UUID (Primary Keys) : SUCCESS")
except Exception as ex:
logger.error("Error while migrating Data from Int to UUID (Primary Keys)")
logger.error(ex)
Console.error("Error while migrating Data from Int to UUID (Primary Keys)")
Console.error(ex)
last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count())
last_migration.delete()
return
return
def rollback(migrator: Migrator, database, **kwargs):
"""
Write your rollback migrations here.
"""
db = database
# Condition to prevent running rollback each time we've got a rollback to do
this_migration = MigrateHistory.get_or_none(
MigrateHistory.name == "20240217_rework_servers_uuid_part2"
)
if this_migration is None:
Console.debug("Update database already done, skipping this part")
return
# **********************************************************************************
# Servers New Model from Old (easier to migrate without dunmping Database)
# **********************************************************************************
class Servers(peewee.Model):
server_id = peewee.CharField(primary_key=True, default=str(uuid.uuid4()))
created = peewee.DateTimeField(default=datetime.datetime.now)
server_uuid = peewee.CharField(default="", index=True)
server_name = peewee.CharField(default="Server", index=True)
path = peewee.CharField(default="")
backup_path = peewee.CharField(default="")
executable = peewee.CharField(default="")
log_path = peewee.CharField(default="")
execution_command = peewee.CharField(default="")
auto_start = peewee.BooleanField(default=0)
auto_start_delay = peewee.IntegerField(default=10)
crash_detection = peewee.BooleanField(default=0)
stop_command = peewee.CharField(default="stop")
executable_update_url = peewee.CharField(default="")
server_ip = peewee.CharField(default="127.0.0.1")
server_port = peewee.IntegerField(default=25565)
logs_delete_after = peewee.IntegerField(default=0)
type = peewee.CharField(default="minecraft-java")
show_status = peewee.BooleanField(default=1)
created_by = peewee.IntegerField(default=-100)
shutdown_timeout = peewee.IntegerField(default=60)
ignored_exits = peewee.CharField(default="0")
class Meta:
table_name = "servers"
database = db
try:
logger.info("Migrating Data from UUID to Int (Primary Keys)")
Console.info("Migrating Data from UUID to Int (Primary Keys)")
# Migrating servers from the old id type to the new one
new_id = 0
for server in Servers.select():
new_id += 1
Servers.update(server_uuid=server.server_id).where(
Servers.server_id == server.server_id
).execute()
Servers.update(server_id=new_id).where(
Servers.server_id == server.server_id
).execute()
logger.info("Migrating Data from UUID to Int (Primary Keys) : SUCCESS")
Console.info("Migrating Data from UUID to Int (Primary Keys) : SUCCESS")
except Exception as ex:
logger.error("Error while migrating Data from UUID to Int (Primary Keys)")
logger.error(ex)
Console.error("Error while migrating Data from UUID to Int (Primary Keys)")
Console.error(ex)
last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count())
last_migration.delete()
return
try:
logger.info("Migrating Data from UUID to Int (Foreign Keys)")
Console.info("Migrating Data from UUID to Int (Foreign Keys)")
# Changes on Audit Log Table
for audit_log in AuditLog.select():
old_server_id = audit_log.server_id_id
if old_server_id is None:
new_server_id = 0
else:
try:
server = Servers.get_or_none(Servers.server_uuid == old_server_id)
new_server_id = server.server_id
except:
new_server_id = old_server_id
AuditLog.update(server_id=new_server_id).where(
AuditLog.audit_id == audit_log.audit_id
).execute()
# Changes on Webhooks Log Table
for webhook in Webhooks.select():
old_server_id = webhook.server_id_id
try:
server = Servers.get_or_none(Servers.server_uuid == old_server_id)
new_server_id = server.server_id
except:
new_server_id = old_server_id
Webhooks.update(server_id=new_server_id).where(
Webhooks.id == webhook.id
).execute()
# Changes on Schedules Log Table
for schedule in Schedules.select():
old_server_id = schedule.server_id_id
try:
server = Servers.get_or_none(Servers.server_uuid == old_server_id)
new_server_id = server.server_id
except:
new_server_id = old_server_id
Schedules.update(server_id=new_server_id).where(
Schedules.schedule_id == schedule.schedule_id
).execute()
# Changes on Backups Log Table
for backup in Backups.select():
old_server_id = backup.server_id_id
try:
server = Servers.get_or_none(Servers.server_uuid == old_server_id)
new_server_id = server.server_id
except:
new_server_id = old_server_id
Backups.update(server_id=new_server_id).where(
Backups.server_id == old_server_id
).execute()
# Changes on RoleServers Log Table
for role_servers in RoleServers.select():
old_server_id = role_servers.server_id_id
try:
server = Servers.get_or_none(Servers.server_uuid == old_server_id)
new_server_id = server.server_id
except:
new_server_id = old_server_id
RoleServers.update(server_id=new_server_id).where(
RoleServers.role_id == role_servers.id
and RoleServers.server_id == old_server_id
).execute()
logger.info("Migrating Data from UUID to Int (Foreign Keys) : SUCCESS")
Console.info("Migrating Data from UUID to Int (Foreign Keys) : SUCCESS")
except Exception as ex:
logger.error("Error while migrating Data from UUID to Int (Foreign Keys)")
logger.error(ex)
Console.error("Error while migrating Data from UUID to Int (Foreign Keys)")
Console.error(ex)
last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count())
last_migration.delete()
return
return

View File

@ -7,6 +7,7 @@ from app.classes.shared.console import Console
from app.classes.shared.migration import Migrator, MigrateHistory from app.classes.shared.migration import Migrator, MigrateHistory
from app.classes.models.management import Schedules, Backups from app.classes.models.management import Schedules, Backups
from app.classes.models.server_permissions import RoleServers from app.classes.models.server_permissions import RoleServers
from app.classes.models.servers import Servers
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -17,40 +18,7 @@ def migrate(migrator: Migrator, database, **kwargs):
""" """
db = database db = database
# **********************************************************************************
# Servers New Model from Old (easier to migrate without dunmping Database)
# **********************************************************************************
class Servers(peewee.Model):
server_id = peewee.CharField(primary_key=True, default=str(uuid.uuid4()))
created = peewee.DateTimeField(default=datetime.datetime.now)
server_name = peewee.CharField(default="Server", index=True)
path = peewee.CharField(default="")
backup_path = peewee.CharField(default="")
executable = peewee.CharField(default="")
log_path = peewee.CharField(default="")
execution_command = peewee.CharField(default="")
auto_start = peewee.BooleanField(default=0)
auto_start_delay = peewee.IntegerField(default=10)
crash_detection = peewee.BooleanField(default=0)
stop_command = peewee.CharField(default="stop")
executable_update_url = peewee.CharField(default="")
server_ip = peewee.CharField(default="127.0.0.1")
server_port = peewee.IntegerField(default=25565)
logs_delete_after = peewee.IntegerField(default=0)
type = peewee.CharField(default="minecraft-java")
show_status = peewee.BooleanField(default=1)
created_by = peewee.IntegerField(default=-100)
shutdown_timeout = peewee.IntegerField(default=60)
ignored_exits = peewee.CharField(default="0")
class Meta:
table_name = "servers"
database = db
try: try:
logger.info("Migrating Data from Int to UUID (Fixing Issue)")
Console.info("Migrating Data from Int to UUID (Fixing Issue)")
# Changes on Servers Roles Table # Changes on Servers Roles Table
migrator.alter_column_type( migrator.alter_column_type(
RoleServers, RoleServers,
@ -87,10 +55,13 @@ def migrate(migrator: Migrator, database, **kwargs):
), ),
) )
migrator.run() # Drop Column after migration
servers_columns = db.get_columns("servers")
logger.info("Migrating Data from Int to UUID (Fixing Issue) : SUCCESS") if any(column_data.name == "server_uuid" for column_data in servers_columns):
Console.info("Migrating Data from Int to UUID (Fixing Issue) : SUCCESS") Console.debug(
"Servers.server_uuid not deleted before Crafty version 4.3.2, skipping this part"
)
migrator.drop_columns("servers", ["server_uuid"])
except Exception as ex: except Exception as ex:
logger.error("Error while migrating Data from Int to UUID (Fixing Issue)") logger.error("Error while migrating Data from Int to UUID (Fixing Issue)")
@ -130,3 +101,7 @@ def rollback(migrator: Migrator, database, **kwargs):
"server_id", "server_id",
peewee.IntegerField(null=True), peewee.IntegerField(null=True),
) )
migrator.add_columns(
"servers", server_uuid=peewee.CharField(default="", index=True)
) # Recreating the column for roll back

View File

@ -53,6 +53,20 @@
"translationTitle": "Překlad do jazyků", "translationTitle": "Překlad do jazyků",
"translator": "Překladatelé" "translator": "Překladatelé"
}, },
"customLogin": {
"apply": "použít",
"backgroundUpload": "Nahrání pozadí",
"customLoginPage": "Udělej si vlastní přihlašovací stránku",
"delete": "Smazat",
"labelLoginImage": "Vyber si svoji přihlašovací stránku",
"loginBackground": "Pozadí pro přihlášeni",
"loginImage": "Nahrání pozadí pro přihlašovací stránku.",
"loginOpacity": "Nastavení transparentnosti přihlašovacího okna",
"pageTitle": "Vlastní přihlašovací stránka",
"preview": "Náhled",
"select": "Vybrat",
"selectImage": "Vybrat obrázek"
},
"dashboard": { "dashboard": {
"actions": "Akce", "actions": "Akce",
"allServers": "Všechny servery", "allServers": "Všechny servery",
@ -97,6 +111,7 @@
"starting": "Zpožděný start", "starting": "Zpožděný start",
"status": "Stav", "status": "Stav",
"stop": "Zastavit", "stop": "Zastavit",
"storage": "úložiště",
"version": "Verze", "version": "Verze",
"welcome": "Vítejte v Crafty Controlleru" "welcome": "Vítejte v Crafty Controlleru"
}, },
@ -181,8 +196,11 @@
}, },
"error": { "error": {
"agree": "Souhlasím", "agree": "Souhlasím",
"bedrockError": "Stažení Bedrocku není dostupné. Prosím zkontrolujte",
"cancel": "Zrušit", "cancel": "Zrušit",
"contact": "Kontaktujte podporu Crafty přes Discord", "contact": "Kontaktujte podporu Crafty přes Discord",
"craftyStatus": "Crafty stav systémů",
"cronFormat": "Neplatný formát Cron",
"embarassing": "Ach jo, no, to je trapné.", "embarassing": "Ach jo, no, to je trapné.",
"error": "Chyba!", "error": "Chyba!",
"eulaAgree": "Souhlasíte?", "eulaAgree": "Souhlasíte?",
@ -191,12 +209,17 @@
"fileError": "Typ souboru musí být obrázek.", "fileError": "Typ souboru musí být obrázek.",
"fileTooLarge": "Odeslání se nezdařilo. Příliš velký nahraný soubor. Obraťte se na správce systému.", "fileTooLarge": "Odeslání se nezdařilo. Příliš velký nahraný soubor. Obraťte se na správce systému.",
"hereIsTheError": "Zde je chyba", "hereIsTheError": "Zde je chyba",
"installerJava": "Selhala instalace {} : Forge servery potřebují k instalaci Javu. Zjistili jsme že java není nainstalovaná. Prosím nainstalujte java na server.",
"internet": "Zjistili jsme, že počítač se spuštěným programem Crafty není připojen k internetu. Připojení klientů k serveru může být omezeno.", "internet": "Zjistili jsme, že počítač se spuštěným programem Crafty není připojen k internetu. Připojení klientů k serveru může být omezeno.",
"migration": "Crafty's hlavní úložiště se přesouvá do jiné lokace. Všechny servry budou pozastaveny v tento čas. Prosím počkejte než se dokončí migrace",
"no-file": "Zdá se, že nemůžeme najít požadovaný soubor. Překontrolujte cestu. Má Crafty správné oprávnění?", "no-file": "Zdá se, že nemůžeme najít požadovaný soubor. Překontrolujte cestu. Má Crafty správné oprávnění?",
"noInternet": "Crafty se nemůže připojit k internetu. Vytváření serverů je pozastaveno. Prosím zkontrolujte připojení k internetu a potom obnovte tuto stránku.",
"noJava": "Server {} se nepodařilo spustit s kódem chyby: Zjistili jsme, že Java není nainstalována. Nainstalujte prosím Javu a poté spusťte server.", "noJava": "Server {} se nepodařilo spustit s kódem chyby: Zjistili jsme, že Java není nainstalována. Nainstalujte prosím Javu a poté spusťte server.",
"not-downloaded": "Zdá se, že nemůžeme najít váš spustitelný soubor. Bylo jeho stahování dokončeno? Jsou oprávnění nastavena na spustitelný soubor?", "not-downloaded": "Zdá se, že nemůžeme najít váš spustitelný soubor. Bylo jeho stahování dokončeno? Jsou oprávnění nastavena na spustitelný soubor?",
"portReminder": "Zjistili jsme, že server {} byl spuštěn poprvé. Ujistěte se, že jste přesměrovali port {} přes váš směrovač/firewall, aby byl tento port vzdáleně přístupný z internetu.", "portReminder": "Zjistili jsme, že server {} byl spuštěn poprvé. Ujistěte se, že jste přesměrovali port {} přes váš směrovač/firewall, aby byl tento port vzdáleně přístupný z internetu.",
"privMsg": "a ", "privMsg": "a ",
"serverJars1": "Server JAR api je nepřístupná. Prosím zkontrolujte",
"serverJars2": "pro aktualní informace.",
"start-error": "Server {} se nepodařilo spustit s kódem chyby: {}", "start-error": "Server {} se nepodařilo spustit s kódem chyby: {}",
"superError": "Pro dokončení této akce musíte být super uživatel.", "superError": "Pro dokončení této akce musíte být super uživatel.",
"terribleFailure": "Jaké strašné selhání!" "terribleFailure": "Jaké strašné selhání!"
@ -207,10 +230,14 @@
"version": "Verze" "version": "Verze"
}, },
"login": { "login": {
"defaultPath": "Heslo které jste vložili je výchozí cesta k pověření. Prosím najděte výchozí heslo v této lokaci.",
"disabled": "Uživatelský účet byl pozastaven. Prosím kontaktujte vašeho správce systému pro více informací.",
"forgotPassword": "Zapomenuté heslo", "forgotPassword": "Zapomenuté heslo",
"incorrect": "Nesprávné uživatelské jméno nebo heslo",
"login": "Přihlásit se", "login": "Přihlásit se",
"password": "Heslo", "password": "Heslo",
"username": "Uživatelské jméno" "username": "Uživatelské jméno",
"viewStatus": "Podívejte se na statusy systému"
}, },
"notify": { "notify": {
"activityLog": "Záznamy o činnosti", "activityLog": "Záznamy o činnosti",
@ -222,21 +249,32 @@
"preparingLogs": " Počkejte prosím, než připravíme vaše protokoly... Až budou připraveny, pošleme vám oznámení. U rozsáhlých zavádění to může chvíli trvat.", "preparingLogs": " Počkejte prosím, než připravíme vaše protokoly... Až budou připraveny, pošleme vám oznámení. U rozsáhlých zavádění to může chvíli trvat.",
"supportLogs": "Protokoly podpory" "supportLogs": "Protokoly podpory"
}, },
"offline": {
"offline": "Offline",
"pleaseConnect": "Prosím připojte se k internetu ať můžete používat Crafty."
},
"panelConfig": { "panelConfig": {
"adminControls": "Ovládání správce", "adminControls": "Ovládání správce",
"allowedServers": "Povolené servery", "allowedServers": "Povolené servery",
"apply": "Použít",
"assignedRoles": "Přidělené role", "assignedRoles": "Přidělené role",
"backgroundUpload": "Nahrání pozadí", "backgroundUpload": "Nahrání pozadí",
"cancel": "Zrušit", "cancel": "Zrušit",
"clearComms": "Vymazat nevykonané příkazy", "clearComms": "Vymazat nevykonané příkazy",
"custom": "Upravit Crafty",
"delete": "Smazat", "delete": "Smazat",
"edit": "Upravit", "edit": "Upravit",
"enableLang": "Povolit všechny jazyky",
"enabled": "Zapnuto", "enabled": "Zapnuto",
"globalExplain": "Kde má Crafty ukládat všechny soubory serverů. (Cestu doplníme o /server/[uuid serveru])",
"globalServer": "Globalní cesta k serverům",
"json": "Config.json",
"loginBackground": "Přihlašovací obrázek na pozadí", "loginBackground": "Přihlašovací obrázek na pozadí",
"loginImage": "Nahrajte obrázek na pozadí přihlašovací obrazovky.", "loginImage": "Nahrajte obrázek na pozadí přihlašovací obrazovky.",
"match": "Hesla musí být stejná", "match": "Hesla musí být stejná",
"newRole": "Přidat novou roli", "newRole": "Přidat novou roli",
"newUser": "Přidat nového uživatele", "newUser": "Přidat nového uživatele",
"noMounts": "Ukazatel zaplněnosti disků na hlavní stránce",
"pageTitle": "Nastavení panelu", "pageTitle": "Nastavení panelu",
"preview": "Náhled", "preview": "Náhled",
"role": "Role", "role": "Role",
@ -247,6 +285,7 @@
"selectImage": "Vyberte obrázek", "selectImage": "Vyberte obrázek",
"superConfirm": "Postupujte pouze v případě, že chcete, aby měl tento uživatel přístup ke VŠEM (ke všem uživatelským účtům, serverům, nastavení panelu atd.). Může vám dokonce odebrat práva superuživatele.", "superConfirm": "Postupujte pouze v případě, že chcete, aby měl tento uživatel přístup ke VŠEM (ke všem uživatelským účtům, serverům, nastavení panelu atd.). Může vám dokonce odebrat práva superuživatele.",
"superConfirmTitle": "Povolit superuživatele? Jste si jisti?", "superConfirmTitle": "Povolit superuživatele? Jste si jisti?",
"title": "Crafty Konfigurace",
"user": "Uživatel", "user": "Uživatel",
"users": "Uživatelé" "users": "Uživatelé"
}, },
@ -276,9 +315,11 @@
"serversDesc": "servery, ke kterým má tato role přístup" "serversDesc": "servery, ke kterým má tato role přístup"
}, },
"serverBackups": { "serverBackups": {
"after": "Spustit příkaz po záloze",
"backupAtMidnight": "Automatické zálohování o půlnoci?", "backupAtMidnight": "Automatické zálohování o půlnoci?",
"backupNow": "Zálohovat nyní!", "backupNow": "Zálohovat nyní!",
"backupTask": "Bylo spuštěno zálohování.", "backupTask": "Bylo spuštěno zálohování.",
"before": "Spustit příkaz před zálohou",
"cancel": "Zrušit", "cancel": "Zrušit",
"clickExclude": "Kliknutím vyberete výjimku", "clickExclude": "Kliknutím vyberete výjimku",
"compress": "Komprimovat zálohu", "compress": "Komprimovat zálohu",
@ -309,6 +350,7 @@
"bePatientDeleteFiles": "Buďte prosím trpěliví, než odstraníme váš server z panelu Crafty a všechny jeho soubory. Tato obrazovka se za chvíli zavře.", "bePatientDeleteFiles": "Buďte prosím trpěliví, než odstraníme váš server z panelu Crafty a všechny jeho soubory. Tato obrazovka se za chvíli zavře.",
"bePatientUpdate": "Prosím, buďte trpěliví, dokud server neaktualizujeme. Doba stahování se může lišit v závislosti na rychlosti vašeho internetu.<br /> Tato obrazovka se za chvíli aktualizuje", "bePatientUpdate": "Prosím, buďte trpěliví, dokud server neaktualizujeme. Doba stahování se může lišit v závislosti na rychlosti vašeho internetu.<br /> Tato obrazovka se za chvíli aktualizuje",
"cancel": "Zrušit", "cancel": "Zrušit",
"countPlayers": "Počítat server v globálních statistikách",
"crashTime": "Časový limit havárie", "crashTime": "Časový limit havárie",
"crashTimeDesc": "Jak dlouho bychom měli čekat, než budeme váš server považovat za havarovaný?", "crashTimeDesc": "Jak dlouho bychom měli čekat, než budeme váš server považovat za havarovaný?",
"deleteFilesQuestion": "Odstranit soubory serveru z přístroje?", "deleteFilesQuestion": "Odstranit soubory serveru z přístroje?",
@ -318,6 +360,8 @@
"deleteServerQuestionMessage": "Opravdu chcete tento server odstranit? Po tomto kroku již není cesty zpět...", "deleteServerQuestionMessage": "Opravdu chcete tento server odstranit? Po tomto kroku již není cesty zpět...",
"exeUpdateURL": "Adresa URL pro aktualizaci spustitelných souborů serveru", "exeUpdateURL": "Adresa URL pro aktualizaci spustitelných souborů serveru",
"exeUpdateURLDesc": "Adresa URL pro přímé stahování aktualizací.", "exeUpdateURLDesc": "Adresa URL pro přímé stahování aktualizací.",
"ignoredExits": "Ignorovat kódy pádu serveru",
"ignoredExitsExplain": "Ukončovací kódy kteŕe Crafty bude ignorovat a brát jako normálně ukončený server (odělte čárkou)",
"javaNoChange": "Nepřepisujte", "javaNoChange": "Nepřepisujte",
"javaVersion": "Přepsat aktuální verzi Javy", "javaVersion": "Přepsat aktuální verzi Javy",
"javaVersionDesc": "Pokud se chystáte přepsat Javu, ujistěte se, že je aktuální cesta k Javě v příkazu 'execution command' zabalena do uvozovek (výchozí proměnná 'java' je vyloučena).", "javaVersionDesc": "Pokud se chystáte přepsat Javu, ujistěte se, že je aktuální cesta k Javě v příkazu 'execution command' zabalena do uvozovek (výchozí proměnná 'java' je vyloučena).",
@ -350,6 +394,8 @@
"serverStopCommandDesc": "Příkaz k odeslání programu pro jeho zastavení", "serverStopCommandDesc": "Příkaz k odeslání programu pro jeho zastavení",
"showStatus": "Zobrazit na veřejné stavové stránce", "showStatus": "Zobrazit na veřejné stavové stránce",
"shutdownTimeout": "Časový limit pro vypnutí", "shutdownTimeout": "Časový limit pro vypnutí",
"statsHint1": "Zde by měl být uveden port, na kterém běží váš serv.er. Tímto způsobem Crafty otevře spojení s vaším serverem pro statistiky.",
"statsHint2": "Toto nezmění tvůj port pro tvůj server. Musíš to také změnit v konfiguračním souboru tvého serveru.",
"stopBeforeDeleting": "Před odstraněním serveru jej prosím zastavte", "stopBeforeDeleting": "Před odstraněním serveru jej prosím zastavte",
"timeoutExplain1": "Jak dlouho bude Crafty čekat na vypnutí serveru po provedení příkazu", "timeoutExplain1": "Jak dlouho bude Crafty čekat na vypnutí serveru po provedení příkazu",
"timeoutExplain2": "než proces ukončí.", "timeoutExplain2": "než proces ukončí.",
@ -478,8 +524,10 @@
"every": "Každý", "every": "Každý",
"interval": "Interval", "interval": "Interval",
"name": "Název", "name": "Název",
"newSchedule": "Nový úkol",
"nextRun": "Příští spuštění", "nextRun": "Příští spuštění",
"no": "Ne", "no": "Ne",
"no-schedule": "Teď tu nejsou žádné úkoly pro tento server. Pro nový úkol klini",
"scheduledTasks": "Naplánované úlohy", "scheduledTasks": "Naplánované úlohy",
"yes": "Ano" "yes": "Ano"
}, },
@ -487,6 +535,7 @@
"cpuUsage": "Využití CPU", "cpuUsage": "Využití CPU",
"description": "Popis", "description": "Popis",
"errorCalculatingUptime": "Chyba při výpočtu doby provozu", "errorCalculatingUptime": "Chyba při výpočtu doby provozu",
"loadingMotd": "Načítaní MOTD",
"memUsage": "Využití paměti", "memUsage": "Využití paměti",
"offline": "Offline", "offline": "Offline",
"online": "Online", "online": "Online",
@ -529,6 +578,7 @@
"importServerButton": "Importovat server!", "importServerButton": "Importovat server!",
"importZip": "Imporovat ze souboru Zip", "importZip": "Imporovat ze souboru Zip",
"importing": "Importování serveru...", "importing": "Importování serveru...",
"labelZipFile": "Vybrat zip soubor",
"maxMem": "Maximální paměť", "maxMem": "Maximální paměť",
"minMem": "Minimální paměť", "minMem": "Minimální paměť",
"myNewServer": "Nový server", "myNewServer": "Nový server",
@ -549,8 +599,11 @@
"serverPort": "Port serveru", "serverPort": "Port serveru",
"serverSelect": "Výběr serveru", "serverSelect": "Výběr serveru",
"serverType": "Typ serveru", "serverType": "Typ serveru",
"serverUpload": "Nahrát zazipovány Server",
"serverVersion": "Verze serveru", "serverVersion": "Verze serveru",
"sizeInGB": "Velikost v GB", "sizeInGB": "Velikost v GB",
"unsupported": "Verze Minecraftu nižší než 1.8 nejsou v Crafty podporovány. Přesto si ji můžete nainstalovat. Není zaručena funkčnost",
"uploadButton": "Nahrát",
"uploadZip": "Nahrání souboru Zip pro importování serveru", "uploadZip": "Nahrání souboru Zip pro importování serveru",
"zipPath": "Cesta k serveru" "zipPath": "Cesta k serveru"
}, },
@ -559,10 +612,20 @@
"credits": "Zásluhy", "credits": "Zásluhy",
"dashboard": "Ovládací panel", "dashboard": "Ovládací panel",
"documentation": "Dokumentace", "documentation": "Dokumentace",
"inApp": "V app dokumentaci",
"navigation": "Navigace", "navigation": "Navigace",
"newServer": "Vytvořit nový server", "newServer": "Vytvořit nový server",
"servers": "Servery" "servers": "Servery"
}, },
"startup": {
"almost": "Dokončuji. Držte se...",
"internals": "Nastavuji a startuji Crafty interní komponenty",
"internet": "Kontroluju připojení k internetu",
"server": "Konfigurace ",
"serverInit": "Konfigurace serveru",
"starting": "Crafty se startuje...",
"tasks": "Startuji plány"
},
"userConfig": { "userConfig": {
"apiKey": "Klíče API", "apiKey": "Klíče API",
"auth": "Autorizovaný? ", "auth": "Autorizovaný? ",
@ -602,5 +665,30 @@
"userSettings": "Nastavení uživatele", "userSettings": "Nastavení uživatele",
"userTheme": "Motiv UI", "userTheme": "Motiv UI",
"uses": "Počet povolených použití (-1==bez omezení)" "uses": "Počet povolených použití (-1==bez omezení)"
},
"webhooks": {
"areYouSureDel": "Seš si jistý že chceš smazat tento webhook?",
"areYouSureRun": "Seš si jistý že chceš otestovat tento webhook?",
"backup_server": "Záloha serveru je hotova.",
"bot_name": "Název bota",
"color": "Vyberte barvu ",
"crash_detected": "Server spadl",
"edit": "Upravit",
"enabled": "Aktivní",
"jar_update": "Server byl aktualizován",
"kill": "Server byl násilně ukončen",
"name": "Název",
"new": "Nový Webhook",
"newWebhook": "Nový Webhook",
"no-webhook": "Teď tu nejsou žádné webhooky pro tento server. Pro nový webhook klikni.",
"run": "Spustit test webhooku",
"send_command": "Server příkaz obdržel",
"start_server": "Server zapnut",
"stop_server": "Server vypnut",
"trigger": "Spustit",
"type": "Typ webhooku",
"url": "Webhook URL",
"webhook_body": "Webhook Body",
"webhooks": "Webhooky"
} }
} }

View File

@ -53,6 +53,20 @@
"translationTitle": "Traduttore in lingua", "translationTitle": "Traduttore in lingua",
"translator": "Traduttori" "translator": "Traduttori"
}, },
"customLogin": {
"apply": "Applica",
"backgroundUpload": "Carica sfondo",
"customLoginPage": "Personalizza la pagina di accesso",
"delete": "Cancella",
"labelLoginImage": "Scegli lo sfondo della finestra di accesso",
"loginBackground": "Immagini di sfondo della finestra di accesso",
"loginImage": "Carica un'immagine di sfondo per la schermata di accesso.",
"loginOpacity": "Seleziona l'opacità della finestra di accesso",
"pageTitle": "Pagina di accesso personalizzata",
"preview": "Anteprima",
"select": "Seleziona",
"selectImage": "Seleziona un'immagine"
},
"dashboard": { "dashboard": {
"actions": "Azioni", "actions": "Azioni",
"allServers": "Tutti i Server", "allServers": "Tutti i Server",
@ -75,6 +89,7 @@
"dashboard": "Pannello di Controllo", "dashboard": "Pannello di Controllo",
"delay-explained": "Il servizio/agente è stato avviato di recente e sta ritardando l'avvio del server di Minecraft", "delay-explained": "Il servizio/agente è stato avviato di recente e sta ritardando l'avvio del server di Minecraft",
"host": "Host", "host": "Host",
"installing": "Installazione...",
"kill": "Termina il Processo", "kill": "Termina il Processo",
"killing": "Terminando il processo...", "killing": "Terminando il processo...",
"lastBackup": "Ultimo:", "lastBackup": "Ultimo:",
@ -96,6 +111,7 @@
"starting": "Avvio ritardato", "starting": "Avvio ritardato",
"status": "Stato", "status": "Stato",
"stop": "Stop", "stop": "Stop",
"storage": "Archiviazione",
"version": "Versione", "version": "Versione",
"welcome": "Benvenuto su Crafty Controller" "welcome": "Benvenuto su Crafty Controller"
}, },
@ -164,20 +180,33 @@
} }
}, },
"error": { "error": {
"agree": "Conferma",
"bedrockError": "I download di Bedrock non sono disponibili. Si prega di controllare",
"cancel": "Annulla",
"contact": "Contact Crafty Control Support via Discord", "contact": "Contact Crafty Control Support via Discord",
"craftyStatus": "Stato di Crafty",
"cronFormat": "Rilevato formato Cron non valido",
"embarassing": "Oh my, well, this is embarrassing.", "embarassing": "Oh my, well, this is embarrassing.",
"error": "Error!", "error": "Error!",
"eulaAgree": "Do you agree?", "eulaAgree": "Do you agree?",
"eulaMsg": "You must agree to the EULA. A copy of the Minecraft EULA is linked under this message.", "eulaMsg": "You must agree to the EULA. A copy of the Minecraft EULA is linked under this message.",
"eulaTitle": "Agree To EULA", "eulaTitle": "Agree To EULA",
"fileError": "Il tipo di file deve essere un'immagine.",
"fileTooLarge": "Caricamento fallito. File da caricare troppo grande. Contatta un amministratore di sistema per assistenza.", "fileTooLarge": "Caricamento fallito. File da caricare troppo grande. Contatta un amministratore di sistema per assistenza.",
"hereIsTheError": "Here is the error", "hereIsTheError": "Here is the error",
"installerJava": "Installazione fallita {} : L'installazione di server Forge richiede Java. Abbiamo rilevato che Java non è installato. Installa Java e riprova.",
"internet": "We have detected the machine running Crafty has no connection to the internet. Client connections to the server may be limited.", "internet": "We have detected the machine running Crafty has no connection to the internet. Client connections to the server may be limited.",
"migration": "Lo spazio di archiviazione del server principale di Crafty verrà migrato in una nuova posizione. Durante questo periodo tutti gli avvii dei server sono stati sospesi. Aspetta mentre completiamo la migrazione",
"no-file": "We can't seem to locate the requested file. Double check the path. Does Crafty have proper permissions?", "no-file": "We can't seem to locate the requested file. Double check the path. Does Crafty have proper permissions?",
"noInternet": "Crafty ha problemi ad accedere a internet. La creazione di server è stata disabilitata. Controlla la tua connessione a internet e aggiorna la pagina.",
"noJava": "Server {} failed to start with error code: We have detected Java is not installed. Please install java then start the server.", "noJava": "Server {} failed to start with error code: We have detected Java is not installed. Please install java then start the server.",
"not-downloaded": "We can't seem to find your executable file. Has it finished downloading? Are the permissions set to executable?", "not-downloaded": "We can't seem to find your executable file. Has it finished downloading? Are the permissions set to executable?",
"portReminder": "We have detected this is the first time {} has been run. Make sure to forward port {} through your router/firewall to make this remotely accessible from the internet.", "portReminder": "We have detected this is the first time {} has been run. Make sure to forward port {} through your router/firewall to make this remotely accessible from the internet.",
"privMsg": "e il ",
"serverJars1": "API JAR del server non raggiungibile. Si prega di controllare",
"serverJars2": "per informazioni più aggiornate.",
"start-error": "Server {} failed to start with error code: {}", "start-error": "Server {} failed to start with error code: {}",
"superError": "Devi essere un super utente per eseguire questa azione.",
"terribleFailure": "What a Terrible Failure!" "terribleFailure": "What a Terrible Failure!"
}, },
"footer": { "footer": {
@ -186,10 +215,14 @@
"version": "Versione" "version": "Versione"
}, },
"login": { "login": {
"defaultPath": "La password immessa è il percorso delle credenziali predefinite, non la password. Trova la password predefinita in quella posizione.",
"disabled": "Account utente disabilitato. Per ulteriori informazioni, contatta l'amministratore di sistema.",
"forgotPassword": "Password dimenticata", "forgotPassword": "Password dimenticata",
"incorrect": "nome utente o password errati",
"login": "Accedi", "login": "Accedi",
"password": "Password", "password": "Password",
"username": "Nome utente" "username": "Nome utente",
"viewStatus": "Visualizza la pagina di stato pubblica"
}, },
"notify": { "notify": {
"activityLog": "Registri di Attività", "activityLog": "Registri di Attività",
@ -201,24 +234,38 @@
"preparingLogs": "Per favore aspetta mentre prepariamo i tuoi registri... Ti invieremo una notifica quando saranno pronti. Potrebbe volerci un po' per installazioni grosse.", "preparingLogs": "Per favore aspetta mentre prepariamo i tuoi registri... Ti invieremo una notifica quando saranno pronti. Potrebbe volerci un po' per installazioni grosse.",
"supportLogs": "Registri di supporto" "supportLogs": "Registri di supporto"
}, },
"offline": {
"offline": "Disconnesso",
"pleaseConnect": "Connettiti a internet per usare Crafty."
},
"panelConfig": { "panelConfig": {
"adminControls": "Controllo admin", "adminControls": "Controllo admin",
"allowedServers": "Server permessi", "allowedServers": "Server permessi",
"apply": "Applica",
"assignedRoles": "Ruoli assegnati", "assignedRoles": "Ruoli assegnati",
"cancel": "Cancella", "cancel": "Cancella",
"clearComms": "Pulisci i comandi non eseguiti", "clearComms": "Pulisci i comandi non eseguiti",
"custom": "Personalizza Crafty",
"delete": "Elimina", "delete": "Elimina",
"edit": "Modifica", "edit": "Modifica",
"enableLang": "Abilita tutte le lingue",
"enabled": "Abilitato", "enabled": "Abilitato",
"globalExplain": "Dove Crafty memorizza tutti i file del tuo server. (Aggiungeremo il percorso con /servers/[uuid of server])",
"globalServer": "Directory dei server globali",
"json": "Config.json",
"match": "Le password devono corrispondere",
"newRole": "Aggiungi nuovo ruolo", "newRole": "Aggiungi nuovo ruolo",
"newUser": "Aggiungi nuovo utente", "newUser": "Aggiungi nuovo utente",
"noMounts": "Non mostrare supporti su Dash",
"pageTitle": "Configurazioni del pannello", "pageTitle": "Configurazioni del pannello",
"role": "Ruolo", "role": "Ruolo",
"roleUsers": "Utenti del ruolo", "roleUsers": "Utenti del ruolo",
"roles": "Ruoli", "roles": "Ruoli",
"save": "Salva", "save": "Salva",
"select": "Seleziona",
"superConfirm": "Procedi solo se vuoi che questo utente abbia accesso a TUTTO (tutti gli utenti, server, impostazioni del pannello, ecc...). Può anche revocare i tuoi poteri da superuser.", "superConfirm": "Procedi solo se vuoi che questo utente abbia accesso a TUTTO (tutti gli utenti, server, impostazioni del pannello, ecc...). Può anche revocare i tuoi poteri da superuser.",
"superConfirmTitle": "Abilitare lo superuser? Sei sicuro?", "superConfirmTitle": "Abilitare lo superuser? Sei sicuro?",
"title": "Configurazione di Crafty",
"user": "Utente", "user": "Utente",
"users": "Utenti" "users": "Utenti"
}, },
@ -242,14 +289,17 @@
"roleTitle": "Impostazioni del ruolo", "roleTitle": "Impostazioni del ruolo",
"roleUserName": "Nome Utente", "roleUserName": "Nome Utente",
"roleUsers": "Utenti con il ruolo: ", "roleUsers": "Utenti con il ruolo: ",
"selectManager": "Seleziona un gestore per questo ruolo",
"serverAccess": "Abilitato?", "serverAccess": "Abilitato?",
"serverName": "Nome del Server", "serverName": "Nome del Server",
"serversDesc": "Server a cui questo ruolo è consentito l'accesso" "serversDesc": "Server a cui questo ruolo è consentito l'accesso"
}, },
"serverBackups": { "serverBackups": {
"after": "Esegui il comando prima del backup",
"backupAtMidnight": "Auto-backup a mezzanotte?", "backupAtMidnight": "Auto-backup a mezzanotte?",
"backupNow": "Effettua il Backup Ora!", "backupNow": "Effettua il Backup Ora!",
"backupTask": "Un'azione di backup è cominciata.", "backupTask": "Un'azione di backup è cominciata.",
"before": "Esegui il comando dopo il backup",
"cancel": "Cancella", "cancel": "Cancella",
"clickExclude": "Clicca per selezionare le esclusioni", "clickExclude": "Clicca per selezionare le esclusioni",
"compress": "Comprimi backup", "compress": "Comprimi backup",
@ -280,6 +330,7 @@
"bePatientDeleteFiles": "Per favore sii paziente mentre rimuoviamo il tuo server dal pannello di Crafty e cancelliamo tutti i files. Questa schermata si chiuderà in pochi istanti.", "bePatientDeleteFiles": "Per favore sii paziente mentre rimuoviamo il tuo server dal pannello di Crafty e cancelliamo tutti i files. Questa schermata si chiuderà in pochi istanti.",
"bePatientUpdate": "Per favore sii paziente mentre aggiorniamo il server. I tempi di download possono variare dalla tua velocità di internet.<br /> Questa schermata si aggiornerà a breve", "bePatientUpdate": "Per favore sii paziente mentre aggiorniamo il server. I tempi di download possono variare dalla tua velocità di internet.<br /> Questa schermata si aggiornerà a breve",
"cancel": "Cancella", "cancel": "Cancella",
"countPlayers": "Includi il server nel conteggio totale dei giocatori",
"crashTime": "Crash Timeout", "crashTime": "Crash Timeout",
"crashTimeDesc": "Quanto dobbiamo aspettare per considerare il tuo server crashato per colpa di un timeout?", "crashTimeDesc": "Quanto dobbiamo aspettare per considerare il tuo server crashato per colpa di un timeout?",
"deleteFilesQuestion": "Eliminare i file del server dalla macchina?", "deleteFilesQuestion": "Eliminare i file del server dalla macchina?",
@ -289,6 +340,8 @@
"deleteServerQuestionMessage": "Sei sicuro di voler eliminare questo server? Dopo la conferma non puoi tornare indietro...", "deleteServerQuestionMessage": "Sei sicuro di voler eliminare questo server? Dopo la conferma non puoi tornare indietro...",
"exeUpdateURL": "URL di aggiornamento dell'eseguibile del server", "exeUpdateURL": "URL di aggiornamento dell'eseguibile del server",
"exeUpdateURLDesc": "URL di download diretto per gli aggiornamenti del server.", "exeUpdateURLDesc": "URL di download diretto per gli aggiornamenti del server.",
"ignoredExits": "Codici di arresto anomalo ignorati",
"ignoredExitsExplain": "I codici di arresto anomalo per il rilevamento degli incidenti di Crafty dovrebbero essere ignorati come un normale 'stop' (separato da virgole)",
"javaNoChange": "Non cambiare la versione di Java", "javaNoChange": "Non cambiare la versione di Java",
"javaVersion": "Cambia la versione di Java attualmente in uso", "javaVersion": "Cambia la versione di Java attualmente in uso",
"javaVersionDesc": "Se vuoi cambiare versione di Java, assicurati che la path dell'eseguibile di Java sia immessa tra 'apostrofi' ('java' di default è esclusa)", "javaVersionDesc": "Se vuoi cambiare versione di Java, assicurati che la path dell'eseguibile di Java sia immessa tra 'apostrofi' ('java' di default è esclusa)",
@ -319,7 +372,13 @@
"serverPortDesc": "La Porta a cui Crafty si dovrà collegare per le statistiche", "serverPortDesc": "La Porta a cui Crafty si dovrà collegare per le statistiche",
"serverStopCommand": "Comando d'arresto del server", "serverStopCommand": "Comando d'arresto del server",
"serverStopCommandDesc": "Comando inviato al server per l'arresto", "serverStopCommandDesc": "Comando inviato al server per l'arresto",
"showStatus": "Mostra nella pagina di stato pubblica",
"shutdownTimeout": "Tempo di attesa dello spegnimento",
"statsHint1": "la porta che sta usando il server dovrebbe essere qui. Questo è il modo in cui Crafty apre una connessione al tuo server per le statistiche.",
"statsHint2": "Ciò non modifica la porta del tuo server. È comunque necessario modificare la porta nel file di configurazione del server.",
"stopBeforeDeleting": "Per favore arresta il server prima di eliminarlo", "stopBeforeDeleting": "Per favore arresta il server prima di eliminarlo",
"timeoutExplain1": "Per quanto tempo Crafty attenderà lo spegnimento del tuo server dopo averlo eseguito",
"timeoutExplain2": "comando prima di forzare l'arresto del processo.",
"update": "Aggiorna l'eseguibile", "update": "Aggiorna l'eseguibile",
"yesDelete": "Sì, elimina", "yesDelete": "Sì, elimina",
"yesDeleteFiles": "Sì, elimina i files" "yesDeleteFiles": "Sì, elimina i files"
@ -345,8 +404,12 @@
"backup": "Backup", "backup": "Backup",
"config": "Configura", "config": "Configura",
"files": "Files", "files": "Files",
"filter": "Filtra registri",
"filterList": "Parole filtrate",
"logs": "Registri", "logs": "Registri",
"metrics": "Metrica",
"playerControls": "Gestisci i giocatori", "playerControls": "Gestisci i giocatori",
"reset": "Ripristina scorrimento",
"schedule": "Programma", "schedule": "Programma",
"serverDetails": "Dettagli del Server", "serverDetails": "Dettagli del Server",
"terminal": "Terminale" "terminal": "Terminale"
@ -383,6 +446,11 @@
"waitUpload": "Per favore aspetta mentre carichiamo i tuoi files... Potrebbe volerci un momento.", "waitUpload": "Per favore aspetta mentre carichiamo i tuoi files... Potrebbe volerci un momento.",
"yesDelete": "Sì, conosco le conseguenze" "yesDelete": "Sì, conosco le conseguenze"
}, },
"serverMetrics": {
"resetZoom": "Ripristina ingrandimento",
"zoomHint1": "Per ingrandire il grafico, tieni premuto il tasto Maiusc, quindi usa la rotella di scorrimento.",
"zoomHint2": "In alternativa, tieni premuto il tasto Maiusc, quindi fai clic e trascina l'area su cui desideri ingrandire."
},
"serverPlayerManagement": { "serverPlayerManagement": {
"bannedPlayers": "Giocatori Banditi", "bannedPlayers": "Giocatori Banditi",
"loadingBannedPlayers": "Carico i giocatori banditi", "loadingBannedPlayers": "Carico i giocatori banditi",
@ -410,23 +478,44 @@
"parent-explain": "Quale azione dovrebbe far eseguire questa?", "parent-explain": "Quale azione dovrebbe far eseguire questa?",
"reaction": "Reazione", "reaction": "Reazione",
"restart": "Riavvia il Server", "restart": "Riavvia il Server",
"select": "Seleziona Base / Cron / Reazione a catena",
"start": "Avvia il server", "start": "Avvia il server",
"stop": "Arresta il Server", "stop": "Arresta il Server",
"time": "Orario", "time": "Orario",
"time-explain": "A che ora vuoi eseguire la tua azione programmata?" "time-explain": "A che ora vuoi eseguire la tua azione programmata?"
}, },
"serverSchedules": { "serverSchedules": {
"action": "Azione",
"areYouSure": "Eliminare l'azione programmata?", "areYouSure": "Eliminare l'azione programmata?",
"cancel": "Cancella", "cancel": "Cancella",
"cannotSee": "Non vedi tutto?", "cannotSee": "Non vedi tutto?",
"cannotSeeOnMobile": "Prova a cliccare su un'azione programmata per tutti i dettagli.", "cannotSeeOnMobile": "Prova a cliccare su un'azione programmata per tutti i dettagli.",
"child": "Schedario secondario con ID ",
"close": "Chiudi",
"command": "Comando",
"confirm": "Conferma", "confirm": "Conferma",
"confirmDelete": "Vuoi eliminare l'azione programmata? Non puoi tornare indietro." "confirmDelete": "Vuoi eliminare l'azione programmata? Non puoi tornare indietro.",
"create": "Crea nuovo schedario",
"cron": "Stringa Cron",
"delete": "Cancella",
"details": "Dettagli dello schedario",
"edit": "Modifica",
"enabled": "Abilitato",
"every": "Tutto",
"interval": "Intervallo",
"name": "Nome",
"newSchedule": "Nuovo schedario",
"nextRun": "Prossima operazione",
"no": "No",
"no-schedule": "Non sono attualmente presenti schedari per questo server. Per iniziare, clicca",
"scheduledTasks": "Attività schedate",
"yes": "Sì"
}, },
"serverStats": { "serverStats": {
"cpuUsage": "Utilizzo del Processore", "cpuUsage": "Utilizzo del Processore",
"description": "Descrizione", "description": "Descrizione",
"errorCalculatingUptime": "Errore nel calcolo dei tempi di operazione", "errorCalculatingUptime": "Errore nel calcolo dei tempi di operazione",
"loadingMotd": "Caricamento MOTD",
"memUsage": "Utilizzo della memoria", "memUsage": "Utilizzo della memoria",
"offline": "Offline", "offline": "Offline",
"online": "Online", "online": "Online",
@ -444,6 +533,8 @@
"commandInput": "Inserisci il comando", "commandInput": "Inserisci il comando",
"delay-explained": "Il servizio/agente è stato avviato di recente e sta ritardando l'avvio del server di Minecraft", "delay-explained": "Il servizio/agente è stato avviato di recente e sta ritardando l'avvio del server di Minecraft",
"downloading": "Scaricando...", "downloading": "Scaricando...",
"importing": "Importazione...",
"installing": "Installazione...",
"restart": "Riavvia", "restart": "Riavvia",
"sendCommand": "Invia il comando", "sendCommand": "Invia il comando",
"start": "Avvia", "start": "Avvia",
@ -468,6 +559,7 @@
"importServerButton": "Importa Server!", "importServerButton": "Importa Server!",
"importZip": "Importa da un File Zip", "importZip": "Importa da un File Zip",
"importing": "Importando il Server...", "importing": "Importando il Server...",
"labelZipFile": "Scegli il tuo file zip",
"maxMem": "Memoria Massima", "maxMem": "Memoria Massima",
"minMem": "Memoria minima", "minMem": "Memoria minima",
"myNewServer": "Il mio nuovo Server", "myNewServer": "Il mio nuovo Server",
@ -478,6 +570,7 @@
"save": "Salva", "save": "Salva",
"selectRole": "Seleziona i Ruoli", "selectRole": "Seleziona i Ruoli",
"selectRoot": "Seleziona la directory radice dell'archivio", "selectRoot": "Seleziona la directory radice dell'archivio",
"selectServer": "Seleziona un server",
"selectType": "Seleziona un Tipo", "selectType": "Seleziona un Tipo",
"selectVersion": "Seleziona una versione", "selectVersion": "Seleziona una versione",
"selectZipDir": "Seleziona il percorso in memoria in cui scompatteremo i file", "selectZipDir": "Seleziona il percorso in memoria in cui scompatteremo i file",
@ -485,9 +578,14 @@
"serverName": "Nome Server", "serverName": "Nome Server",
"serverPath": "Percorso del Server", "serverPath": "Percorso del Server",
"serverPort": "Porta del Server", "serverPort": "Porta del Server",
"serverSelect": "Server selezionato",
"serverType": "Tipo di Server", "serverType": "Tipo di Server",
"serverUpload": "Carica server zippato",
"serverVersion": "Versione del Server", "serverVersion": "Versione del Server",
"sizeInGB": "Dimensione in GB", "sizeInGB": "Dimensione in GB",
"unsupported": "Le versioni di Minecraft inferiori alla 1.8 non sono supportate da Crafty. Puoi comunque installarlo. I risultati varieranno.",
"uploadButton": "Carica",
"uploadZip": "Carica file zip per l'importazione del server",
"zipPath": "Percorso del Server" "zipPath": "Percorso del Server"
}, },
"sidebar": { "sidebar": {
@ -495,10 +593,20 @@
"credits": "Crediti", "credits": "Crediti",
"dashboard": "Pannello di controllo", "dashboard": "Pannello di controllo",
"documentation": "Documentazione", "documentation": "Documentazione",
"inApp": "Documenti in app",
"navigation": "Navigazione", "navigation": "Navigazione",
"newServer": "Crea un Nuovo Server", "newServer": "Crea un Nuovo Server",
"servers": "Servers" "servers": "Servers"
}, },
"startup": {
"almost": "Finalizzazione. Tieniti forte...",
"internals": "Configurazione e avvio dei componenti interni di Crafty",
"internet": "Controllo connessione a internet",
"server": "Inizializzazione ",
"serverInit": "Inizializzazione server",
"starting": "Avvio di Crafty...",
"tasks": "Avvio attività schedate"
},
"userConfig": { "userConfig": {
"apiKey": "Chiavi API", "apiKey": "Chiavi API",
"auth": "Autorizzato? ", "auth": "Autorizzato? ",
@ -519,6 +627,7 @@
"lastLogin": "Ultimo accesso: ", "lastLogin": "Ultimo accesso: ",
"lastUpdate": "Ultimo aggiornamento: ", "lastUpdate": "Ultimo aggiornamento: ",
"leaveBlank": "Per modificare l'utente senza cambiare la passowrd, lascia il campo in bianco.", "leaveBlank": "Per modificare l'utente senza cambiare la passowrd, lascia il campo in bianco.",
"manager": "Gestore",
"member": "È membro?", "member": "È membro?",
"notExist": "Non puoi eliminare qualcosa che non esiste!", "notExist": "Non puoi eliminare qualcosa che non esiste!",
"pageTitle": "Modifica Utente", "pageTitle": "Modifica Utente",
@ -527,6 +636,7 @@
"permName": "Nome del Permesso", "permName": "Nome del Permesso",
"repeat": "Ripeti la Password", "repeat": "Ripeti la Password",
"roleName": "Nome del ruolo", "roleName": "Nome del ruolo",
"selectManager": "Seleziona un gestore per l'utente",
"super": "Super User", "super": "Super User",
"userLang": "Linguaggio dell'utente", "userLang": "Linguaggio dell'utente",
"userName": "Nome utente", "userName": "Nome utente",
@ -534,6 +644,32 @@
"userRoles": "Ruoli dell'utente", "userRoles": "Ruoli dell'utente",
"userRolesDesc": "Ruoli di cui l'utente fa parte.", "userRolesDesc": "Ruoli di cui l'utente fa parte.",
"userSettings": "Impostazioni Utente", "userSettings": "Impostazioni Utente",
"userTheme": "Tema IU",
"uses": "Numero di usi permessi (-1==Nessun limite)" "uses": "Numero di usi permessi (-1==Nessun limite)"
},
"webhooks": {
"areYouSureDel": "Sei sicuro di voler eliminare questo webhook?",
"areYouSureRun": "Sei sicuro di voler testare questo webhook?",
"backup_server": "Backup del server completato",
"bot_name": "Nome del bot",
"color": "Seleziona l'accentuazione del colore",
"crash_detected": "Il server si è arrestato in modo anomalo",
"edit": "Modifica",
"enabled": "Abilitato",
"jar_update": "Eseguibile del server aggiornato",
"kill": "Server arrestato",
"name": "Nome",
"new": "Nuovo webhook",
"newWebhook": "Nuovo webhook",
"no-webhook": "Attualmente non ci sono webhook in questo server. Per iniziare, clicca",
"run": "Testa il webhook",
"send_command": "Comando del server ricevuto",
"start_server": "Server avviato",
"stop_server": "Server spento",
"trigger": "Attivazione a",
"type": "Tipo di Webhook",
"url": "URL del Webhook",
"webhook_body": "Corpo del Webhook",
"webhooks": "Webhook"
} }
} }

View File

@ -18,5 +18,5 @@ termcolor==1.1
tornado==6.3.3 tornado==6.3.3
tzlocal==5.1 tzlocal==5.1
jsonschema==4.19.1 jsonschema==4.19.1
orjson==3.9.7 orjson==3.9.15
prometheus-client==0.17.1 prometheus-client==0.17.1

View File

@ -3,7 +3,7 @@ sonar.organization=crafty-controller
# This is the name and version displayed in the SonarCloud UI. # This is the name and version displayed in the SonarCloud UI.
sonar.projectName=Crafty 4 sonar.projectName=Crafty 4
sonar.projectVersion=4.3.1 sonar.projectVersion=4.3.2
sonar.python.version=3.9, 3.10, 3.11 sonar.python.version=3.9, 3.10, 3.11
sonar.exclusions=app/migrations/**, app/frontend/static/assets/vendors/** sonar.exclusions=app/migrations/**, app/frontend/static/assets/vendors/**