Merge branch 'dev' into lang/czech-updates

This commit is contained in:
Zedifus 2024-05-09 20:48:42 +01:00
commit 31f7d88c3a
68 changed files with 1124 additions and 589 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

@ -5,7 +5,7 @@ yamllint:
stage: lint stage: lint
image: registry.gitlab.com/pipeline-components/yamllint:latest image: registry.gitlab.com/pipeline-components/yamllint:latest
tags: tags:
- docker - saas-linux-medium-amd64
rules: rules:
- if: "$CODE_QUALITY_DISABLED" - if: "$CODE_QUALITY_DISABLED"
when: never when: never
@ -18,7 +18,7 @@ jsonlint:
stage: lint stage: lint
image: registry.gitlab.com/pipeline-components/jsonlint:latest image: registry.gitlab.com/pipeline-components/jsonlint:latest
tags: tags:
- docker - saas-linux-medium-amd64
rules: rules:
- if: "$CODE_QUALITY_DISABLED" - if: "$CODE_QUALITY_DISABLED"
when: never when: never
@ -33,7 +33,7 @@ black:
stage: lint stage: lint
image: registry.gitlab.com/pipeline-components/black:latest image: registry.gitlab.com/pipeline-components/black:latest
tags: tags:
- docker - saas-linux-medium-amd64
rules: rules:
- if: "$CODE_QUALITY_DISABLED" - if: "$CODE_QUALITY_DISABLED"
when: never when: never
@ -46,7 +46,7 @@ pylint:
stage: lint stage: lint
image: registry.gitlab.com/pipeline-components/pylint:latest image: registry.gitlab.com/pipeline-components/pylint:latest
tags: tags:
- docker - saas-linux-medium-amd64
rules: rules:
- if: "$CODE_QUALITY_DISABLED" - if: "$CODE_QUALITY_DISABLED"
when: never when: never
@ -69,7 +69,7 @@ sonarcloud-check:
name: sonarsource/sonar-scanner-cli:latest name: sonarsource/sonar-scanner-cli:latest
entrypoint: [""] entrypoint: [""]
tags: tags:
- docker - saas-linux-medium-amd64
rules: rules:
- if: "$SONAR_TOKEN == null" - if: "$SONAR_TOKEN == null"
when: never when: never
@ -91,7 +91,7 @@ lang-check:
stage: lint stage: lint
image: alpine:latest image: alpine:latest
tags: tags:
- docker - saas-linux-medium-amd64
rules: rules:
- if: "$CODE_QUALITY_DISABLED" - if: "$CODE_QUALITY_DISABLED"
when: never when: never

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:"
fi for dir in $(find "$directory_path" -type d 2>/dev/null); do
if [ -e "$dir" ]; then
sudo chmod 700 "$dir" && ((dir_count++))
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,15 +1,35 @@
# Changelog # Changelog
## --- [4.3.2] - 2024/TBD ## --- [4.3.3] - 2024/TBD
### Refactor
- Refactor API keys "super user" to "full access" ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/731))
### New features ### New features
TBD TBD
### Bug fixes ### Bug fixes
TBD TBD
### Tweaks ### Tweaks
TBD - Add link to go back to dashboard on error page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/743))
### Lang ### Lang
TBD TBD
<br><br> <br><br>
## --- [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.2 # Crafty Controller 4.3.3
> 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

@ -17,6 +17,10 @@ class ServerPermsController:
def get_server_user_list(server_id): def get_server_user_list(server_id):
return PermissionsServers.get_server_user_list(server_id) return PermissionsServers.get_server_user_list(server_id)
@staticmethod
def get_permissions(permissions_mask):
return PermissionsServers.get_permissions(permissions_mask)
@staticmethod @staticmethod
def list_defined_permissions(): def list_defined_permissions():
permissions_list = PermissionsServers.get_permissions_list() permissions_list = PermissionsServers.get_permissions_list()
@ -47,7 +51,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(
@ -61,6 +65,22 @@ class ServerPermsController:
def get_permissions_mask(role_id, server_id): def get_permissions_mask(role_id, server_id):
return PermissionsServers.get_permissions_mask(role_id, server_id) return PermissionsServers.get_permissions_mask(role_id, server_id)
@staticmethod
def get_lowest_api_perm_mask(user_server_permissions_mask, api_key_permssions_mask):
mask = ""
# If this isn't an API key we'll know the request came from basic
# authentication and ignore the API key permissions mask.
if not api_key_permssions_mask:
return user_server_permissions_mask
for _index, (user_perm, api_perm) in enumerate(
zip(user_server_permissions_mask, api_key_permssions_mask)
):
if user_perm == "1" and api_perm == "1":
mask += "1"
else:
mask += "0"
return mask
@staticmethod @staticmethod
def set_permission( def set_permission(
permission_mask, permission_tested: EnumPermissionsServer, value permission_mask, permission_tested: EnumPermissionsServer, value
@ -82,6 +102,11 @@ class ServerPermsController:
def get_api_key_permissions_list(key: ApiKeys, server_id: str): def get_api_key_permissions_list(key: ApiKeys, server_id: str):
return PermissionsServers.get_api_key_permissions_list(key, server_id) return PermissionsServers.get_api_key_permissions_list(key, server_id)
@staticmethod
def get_user_permissions_mask(user_id: str, server_id: str):
user = HelperUsers.get_user_model(user_id)
return PermissionsServers.get_user_permissions_mask(user, server_id)
@staticmethod @staticmethod
def get_authorized_servers_stats_from_roles(user_id): def get_authorized_servers_stats_from_roles(user_id):
user_roles = HelperUsers.get_user_roles_id(user_id) user_roles = HelperUsers.get_user_roles_id(user_id)

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

@ -187,7 +187,7 @@ class PermissionsCrafty:
@staticmethod @staticmethod
def get_api_key_permissions_list(key: ApiKeys): def get_api_key_permissions_list(key: ApiKeys):
user = HelperUsers.get_user(key.user_id) user = HelperUsers.get_user(key.user_id)
if user["superuser"] and key.superuser: if user["superuser"] and key.full_access:
return PermissionsCrafty.get_permissions_list() return PermissionsCrafty.get_permissions_list()
if user["superuser"]: if user["superuser"]:
# User is superuser but API key isn't # User is superuser but API key isn't

View File

@ -264,7 +264,7 @@ class PermissionsServers:
@staticmethod @staticmethod
def get_api_key_permissions_list(key: ApiKeys, server_id: str): def get_api_key_permissions_list(key: ApiKeys, server_id: str):
user = HelperUsers.get_user(key.user_id) user = HelperUsers.get_user(key.user_id)
if user["superuser"] and key.superuser: if user["superuser"] and key.full_access:
return PermissionsServers.get_permissions_list() return PermissionsServers.get_permissions_list()
roles_list = HelperUsers.get_user_roles_id(user["user_id"]) roles_list = HelperUsers.get_user_roles_id(user["user_id"])
role_server = ( role_server = (

View File

@ -71,7 +71,7 @@ class ApiKeys(BaseModel):
user_id = ForeignKeyField(Users, backref="api_token", index=True) user_id = ForeignKeyField(Users, backref="api_token", index=True)
server_permissions = CharField(default="00000000") server_permissions = CharField(default="00000000")
crafty_permissions = CharField(default="000") crafty_permissions = CharField(default="000")
superuser = BooleanField(default=False) full_access = BooleanField(default=False)
class Meta: class Meta:
table_name = "api_keys" table_name = "api_keys"
@ -408,7 +408,7 @@ class HelperUsers:
def add_user_api_key( def add_user_api_key(
name: str, name: str,
user_id: str, user_id: str,
superuser: bool = False, full_access: bool = False,
server_permissions_mask: t.Optional[str] = None, server_permissions_mask: t.Optional[str] = None,
crafty_permissions_mask: t.Optional[str] = None, crafty_permissions_mask: t.Optional[str] = None,
): ):
@ -426,7 +426,7 @@ class HelperUsers:
if crafty_permissions_mask is not None if crafty_permissions_mask is not None
else {} else {}
), ),
ApiKeys.superuser: superuser, ApiKeys.full_access: full_access,
} }
).execute() ).execute()

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

@ -182,6 +182,7 @@ class BaseHandler(tornado.web.RequestHandler):
t.List[str], t.List[str],
bool, bool,
t.Dict[str, t.Any], t.Dict[str, t.Any],
str,
] ]
]: ]:
try: try:
@ -190,9 +191,10 @@ class BaseHandler(tornado.web.RequestHandler):
) )
superuser = user["superuser"] superuser = user["superuser"]
server_permissions_api_mask = ""
if api_key is not None: if api_key is not None:
superuser = superuser and api_key.superuser superuser = superuser and api_key.full_access
server_permissions_api_mask = api_key.server_permissions
exec_user_role = set() exec_user_role = set()
if superuser: if superuser:
authorized_servers = self.controller.servers.get_all_defined_servers() authorized_servers = self.controller.servers.get_all_defined_servers()
@ -214,6 +216,7 @@ class BaseHandler(tornado.web.RequestHandler):
user["user_id"] user["user_id"]
) )
) )
logger.debug(user["roles"]) logger.debug(user["roles"])
for r in user["roles"]: for r in user["roles"]:
role = self.controller.roles.get_role(r) role = self.controller.roles.get_role(r)
@ -234,6 +237,7 @@ class BaseHandler(tornado.web.RequestHandler):
exec_user_role, exec_user_role,
superuser, superuser,
user, user,
server_permissions_api_mask,
) )
logging.debug("Auth unsuccessful") logging.debug("Auth unsuccessful")
auth_log.error( auth_log.error(

View File

@ -168,7 +168,7 @@ class PanelHandler(BaseHandler):
# Commented out because there is no server access control for API keys, # Commented out because there is no server access control for API keys,
# they just inherit from the host user # they just inherit from the host user
# if api_key is not None: # if api_key is not None:
# superuser = superuser and api_key.superuser # superuser = superuser and api_key.full_access
if server_id is None: if server_id is None:
self.redirect("/panel/error?error=Invalid Server ID") self.redirect("/panel/error?error=Invalid Server ID")
@ -242,7 +242,7 @@ class PanelHandler(BaseHandler):
api_key, _token_data, exec_user = self.current_user api_key, _token_data, exec_user = self.current_user
superuser = exec_user["superuser"] superuser = exec_user["superuser"]
if api_key is not None: if api_key is not None:
superuser = superuser and api_key.superuser superuser = superuser and api_key.full_access
if superuser: # TODO: Figure out a better solution if superuser: # TODO: Figure out a better solution
defined_servers = self.controller.servers.list_defined_servers() defined_servers = self.controller.servers.list_defined_servers()
@ -351,7 +351,7 @@ class PanelHandler(BaseHandler):
"created": api_key.created, "created": api_key.created,
"server_permissions": api_key.server_permissions, "server_permissions": api_key.server_permissions,
"crafty_permissions": api_key.crafty_permissions, "crafty_permissions": api_key.crafty_permissions,
"superuser": api_key.superuser, "full_access": api_key.full_access,
} }
if api_key is not None if api_key is not None
else None else None
@ -1356,6 +1356,9 @@ class PanelHandler(BaseHandler):
page_data["crafty_permissions_all"] = ( page_data["crafty_permissions_all"] = (
self.controller.crafty_perms.list_defined_crafty_permissions() self.controller.crafty_perms.list_defined_crafty_permissions()
) )
page_data["user_crafty_permissions"] = (
self.controller.crafty_perms.get_crafty_permissions_list(user_id)
)
if user_id is None: if user_id is None:
self.redirect("/panel/error?error=Invalid User ID") self.redirect("/panel/error?error=Invalid User ID")
@ -1403,7 +1406,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

@ -18,13 +18,14 @@ class ApiServersServerActionHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.COMMANDS
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.COMMANDS not in server_permissions:
# if the user doesn't have Commands permission, return an error # if the user doesn't have Commands permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
@ -33,6 +34,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 +79,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 +113,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

@ -26,12 +26,14 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler):
auth_data = self.authenticate_user() auth_data = self.authenticate_user()
if not auth_data: if not auth_data:
return return
if ( mask = self.controller.server_perms.get_lowest_api_perm_mask(
EnumPermissionsServer.BACKUP self.controller.server_perms.get_user_permissions_mask(
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.BACKUP not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
self.finish_json(200, self.controller.management.get_backup_config(server_id)) self.finish_json(200, self.controller.management.get_backup_config(server_id))
@ -41,12 +43,14 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler):
backup_conf = self.controller.management.get_backup_config(server_id) backup_conf = self.controller.management.get_backup_config(server_id)
if not auth_data: if not auth_data:
return return
if ( mask = self.controller.server_perms.get_lowest_api_perm_mask(
EnumPermissionsServer.BACKUP self.controller.server_perms.get_user_permissions_mask(
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.BACKUP not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
@ -89,12 +93,14 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler):
auth_data = self.authenticate_user() auth_data = self.authenticate_user()
if not auth_data: if not auth_data:
return return
if ( mask = self.controller.server_perms.get_lowest_api_perm_mask(
EnumPermissionsServer.BACKUP self.controller.server_perms.get_user_permissions_mask(
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.BACKUP not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
@ -203,7 +209,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

@ -42,12 +42,14 @@ class ApiServersServerBackupsIndexHandler(BaseApiHandler):
auth_data = self.authenticate_user() auth_data = self.authenticate_user()
if not auth_data: if not auth_data:
return return
if ( mask = self.controller.server_perms.get_lowest_api_perm_mask(
EnumPermissionsServer.BACKUP self.controller.server_perms.get_user_permissions_mask(
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.BACKUP not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
self.finish_json(200, self.controller.management.get_backup_config(server_id)) self.finish_json(200, self.controller.management.get_backup_config(server_id))
@ -82,13 +84,14 @@ class ApiServersServerBackupsIndexHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.BACKUP
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.BACKUP not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})

View File

@ -80,16 +80,16 @@ class ApiServersServerFilesIndexHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
self.controller.server_perms.get_user_permissions_mask(
auth_data[4]["user_id"], server_id
),
auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if ( if (
EnumPermissionsServer.FILES EnumPermissionsServer.FILES not in server_permissions
not in self.controller.server_perms.get_user_id_permissions_list( and EnumPermissionsServer.BACKUP not in server_permissions
auth_data[4]["user_id"], server_id
)
and EnumPermissionsServer.BACKUP
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id
)
): ):
# if the user doesn't have Files or Backup permission, return an error # if the user doesn't have Files or Backup permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
@ -197,13 +197,14 @@ class ApiServersServerFilesIndexHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.FILES
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.FILES not in server_permissions:
# if the user doesn't have Files permission, return an error # if the user doesn't have Files permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
try: try:
@ -254,13 +255,14 @@ class ApiServersServerFilesIndexHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.FILES
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.FILES not in server_permissions:
# if the user doesn't have Files permission, return an error # if the user doesn't have Files permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
try: try:
@ -307,13 +309,14 @@ class ApiServersServerFilesIndexHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.FILES
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.FILES not in server_permissions:
# if the user doesn't have Files permission, return an error # if the user doesn't have Files permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
try: try:
@ -373,13 +376,14 @@ class ApiServersServerFilesCreateHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.FILES
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.FILES not in server_permissions:
# if the user doesn't have Files permission, return an error # if the user doesn't have Files permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
try: try:
@ -438,13 +442,14 @@ class ApiServersServerFilesCreateHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.FILES
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.FILES not in server_permissions:
# if the user doesn't have Files permission, return an error # if the user doesn't have Files permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
try: try:
@ -504,13 +509,14 @@ class ApiServersServerFilesZipHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.FILES
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.FILES not in server_permissions:
# if the user doesn't have Files permission, return an error # if the user doesn't have Files permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
try: try:

View File

@ -102,13 +102,14 @@ class ApiServersServerIndexHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.CONFIG
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.CONFIG not in server_permissions:
# if the user doesn't have Config permission, return an error # if the user doesn't have Config permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
@ -154,13 +155,14 @@ class ApiServersServerIndexHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.CONFIG
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.CONFIG not in server_permissions:
# if the user doesn't have Config permission, return an error # if the user doesn't have Config permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})

View File

@ -30,13 +30,14 @@ class ApiServersServerLogsHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.LOGS
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.LOGS not in server_permissions:
# if the user doesn't have Logs permission, return an error # if the user doesn't have Logs permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})

View File

@ -16,13 +16,14 @@ class ApiServersServerStdinHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.COMMANDS
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.COMMANDS not in server_permissions:
# if the user doesn't have Commands permission, return an error # if the user doesn't have Commands permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})

View File

@ -78,13 +78,14 @@ class ApiServersServerTasksIndexHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.SCHEDULE
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.SCHEDULE not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
data["server_id"] = server_id data["server_id"] = server_id

View File

@ -54,12 +54,14 @@ class ApiServersServerTasksTaskIndexHandler(BaseApiHandler):
auth_data = self.authenticate_user() auth_data = self.authenticate_user()
if not auth_data: if not auth_data:
return return
if ( mask = self.controller.server_perms.get_lowest_api_perm_mask(
EnumPermissionsServer.SCHEDULE self.controller.server_perms.get_user_permissions_mask(
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.SCHEDULE not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
self.finish_json(200, self.controller.management.get_scheduled_task(task_id)) self.finish_json(200, self.controller.management.get_scheduled_task(task_id))
@ -68,12 +70,14 @@ class ApiServersServerTasksTaskIndexHandler(BaseApiHandler):
auth_data = self.authenticate_user() auth_data = self.authenticate_user()
if not auth_data: if not auth_data:
return return
if ( mask = self.controller.server_perms.get_lowest_api_perm_mask(
EnumPermissionsServer.SCHEDULE self.controller.server_perms.get_user_permissions_mask(
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.SCHEDULE not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
@ -120,13 +124,14 @@ class ApiServersServerTasksTaskIndexHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.SCHEDULE
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.SCHEDULE not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})

View File

@ -38,12 +38,14 @@ class ApiServersServerWebhooksIndexHandler(BaseApiHandler):
auth_data = self.authenticate_user() auth_data = self.authenticate_user()
if not auth_data: if not auth_data:
return return
if ( mask = self.controller.server_perms.get_lowest_api_perm_mask(
EnumPermissionsServer.CONFIG self.controller.server_perms.get_user_permissions_mask(
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.CONFIG not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
self.finish_json( self.finish_json(
@ -81,13 +83,14 @@ class ApiServersServerWebhooksIndexHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.CONFIG
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.CONFIG not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
data["server_id"] = server_id data["server_id"] = server_id

View File

@ -39,12 +39,14 @@ class ApiServersServerWebhooksManagementIndexHandler(BaseApiHandler):
auth_data = self.authenticate_user() auth_data = self.authenticate_user()
if not auth_data: if not auth_data:
return return
if ( mask = self.controller.server_perms.get_lowest_api_perm_mask(
EnumPermissionsServer.CONFIG self.controller.server_perms.get_user_permissions_mask(
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.CONFIG not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
if ( if (
@ -66,12 +68,14 @@ class ApiServersServerWebhooksManagementIndexHandler(BaseApiHandler):
auth_data = self.authenticate_user() auth_data = self.authenticate_user()
if not auth_data: if not auth_data:
return return
if ( mask = self.controller.server_perms.get_lowest_api_perm_mask(
EnumPermissionsServer.CONFIG self.controller.server_perms.get_user_permissions_mask(
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.CONFIG not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
@ -117,13 +121,14 @@ class ApiServersServerWebhooksManagementIndexHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.CONFIG
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.CONFIG not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
@ -159,13 +164,14 @@ class ApiServersServerWebhooksManagementIndexHandler(BaseApiHandler):
if server_id not in [str(x["server_id"]) for x in auth_data[0]]: if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
# if the user doesn't have access to the server, return an error # if the user doesn't have access to the server, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
mask = self.controller.server_perms.get_lowest_api_perm_mask(
if ( self.controller.server_perms.get_user_permissions_mask(
EnumPermissionsServer.CONFIG
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id auth_data[4]["user_id"], server_id
) ),
): auth_data[5],
)
server_permissions = self.controller.server_perms.get_permissions(mask)
if EnumPermissionsServer.CONFIG not in server_permissions:
# if the user doesn't have Schedule permission, return an error # if the user doesn't have Schedule permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
webhook = self.controller.management.get_webhook_by_id(webhook_id) webhook = self.controller.management.get_webhook_by_id(webhook_id)

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(
@ -75,7 +75,7 @@ class ApiUsersUserKeyHandler(BaseApiHandler):
"name": key.name, "name": key.name,
"server_permissions": key.server_permissions, "server_permissions": key.server_permissions,
"crafty_permissions": key.crafty_permissions, "crafty_permissions": key.crafty_permissions,
"superuser": key.superuser, "full_access": key.full_access,
} }
) )
self.finish_json( self.finish_json(
@ -99,7 +99,7 @@ class ApiUsersUserKeyHandler(BaseApiHandler):
"type": "string", "type": "string",
"pattern": "^[01]{3}$", # 8 bits, see EnumPermissionsCrafty "pattern": "^[01]{3}$", # 8 bits, see EnumPermissionsCrafty
}, },
"superuser": {"type": "boolean"}, "full_access": {"type": "boolean"},
}, },
"additionalProperties": False, "additionalProperties": False,
"minProperties": 1, "minProperties": 1,
@ -163,7 +163,7 @@ class ApiUsersUserKeyHandler(BaseApiHandler):
key_id = self.controller.users.add_user_api_key( key_id = self.controller.users.add_user_api_key(
data["name"], data["name"],
user_id, user_id,
data["superuser"], data["full_access"],
data["server_permissions_mask"], data["server_permissions_mask"],
data["crafty_permissions_mask"], data["crafty_permissions_mask"],
) )
@ -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

@ -30,7 +30,7 @@ class ServerHandler(BaseHandler):
) = self.current_user ) = self.current_user
superuser = exec_user["superuser"] superuser = exec_user["superuser"]
if api_key is not None: if api_key is not None:
superuser = superuser and api_key.superuser superuser = superuser and api_key.full_access
if superuser: if superuser:
defined_servers = self.controller.servers.list_defined_servers() defined_servers = self.controller.servers.list_defined_servers()
@ -124,7 +124,7 @@ class ServerHandler(BaseHandler):
"created": api_key.created, "created": api_key.created,
"server_permissions": api_key.server_permissions, "server_permissions": api_key.server_permissions,
"crafty_permissions": api_key.crafty_permissions, "crafty_permissions": api_key.crafty_permissions,
"superuser": api_key.superuser, "full_access": api_key.full_access,
} }
if api_key is not None if api_key is not None
else None else None
@ -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

@ -42,7 +42,7 @@ class UploadHandler(BaseHandler):
if self.upload_type == "server_import": if self.upload_type == "server_import":
superuser = exec_user["superuser"] superuser = exec_user["superuser"]
if api_key is not None: if api_key is not None:
superuser = superuser and api_key.superuser superuser = superuser and api_key.full_access
user_id = exec_user["user_id"] user_id = exec_user["user_id"]
stream_size_value = self.helper.get_setting("stream_size_GB") stream_size_value = self.helper.get_setting("stream_size_GB")
@ -133,7 +133,7 @@ class UploadHandler(BaseHandler):
elif self.upload_type == "background": elif self.upload_type == "background":
superuser = exec_user["superuser"] superuser = exec_user["superuser"]
if api_key is not None: if api_key is not None:
superuser = superuser and api_key.superuser superuser = superuser and api_key.full_access
user_id = exec_user["user_id"] user_id = exec_user["user_id"]
stream_size_value = self.helper.get_setting("stream_size_GB") stream_size_value = self.helper.get_setting("stream_size_GB")
@ -212,7 +212,7 @@ class UploadHandler(BaseHandler):
server_id = self.get_argument("server_id", None) server_id = self.get_argument("server_id", None)
superuser = exec_user["superuser"] superuser = exec_user["superuser"]
if api_key is not None: if api_key is not None:
superuser = superuser and api_key.superuser superuser = superuser and api_key.full_access
user_id = exec_user["user_id"] user_id = exec_user["user_id"]
stream_size_value = self.helper.get_setting("stream_size_GB") stream_size_value = self.helper.get_setting("stream_size_GB")

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": 2 "sub": 3
} }

View File

@ -58,7 +58,7 @@
<!--<th>ID</th>--> <!--<th>ID</th>-->
<th>{{ translate('apiKeys', 'name', data['lang']) }}</th> <th>{{ translate('apiKeys', 'name', data['lang']) }}</th>
<th>{{ translate('apiKeys', 'created', data['lang']) }}</th> <th>{{ translate('apiKeys', 'created', data['lang']) }}</th>
<th>{{ translate('apiKeys', 'superUser', data['lang']) }}</th> <th>{{ translate('apiKeys', 'fullAccess', data['lang']) }}</th>
<th>{{ translate('apiKeys', 'perms', data['lang']) }}</th> <th>{{ translate('apiKeys', 'perms', data['lang']) }}</th>
<th>{{ translate('apiKeys', 'buttons', data['lang']) }}</th> <th>{{ translate('apiKeys', 'buttons', data['lang']) }}</th>
</tr> </tr>
@ -70,7 +70,7 @@
<td>{{ apikey.name }}</td> <td>{{ apikey.name }}</td>
<td>{{ apikey.created.strftime('%d/%m/%Y %H:%M:%S') }}</td> <td>{{ apikey.created.strftime('%d/%m/%Y %H:%M:%S') }}</td>
<td> <td>
{% if apikey.superuser %} {% if apikey.full_access %}
<span class="text-success"> <span class="text-success">
<i class="fas fa-check-square"></i> {{ <i class="fas fa-check-square"></i> {{
translate('apiKeys', 'yes', data['lang']) }} translate('apiKeys', 'yes', data['lang']) }}
@ -148,9 +148,15 @@
}}</label> }}</label>
</td> </td>
<td> <td>
{% if permission in data['user_crafty_permissions'] %}
<input type="checkbox" class="crafty_perm" <input type="checkbox" class="crafty_perm"
id="permission_{{ permission.name }}" id="permission_{{ permission.name }}"
name="permission_{{ permission.name }}" value="1"> name="permission_{{ permission.name }}" value="1">
{% else %}
<input type="checkbox" class="crafty_perm"
id="permission_{{ permission.name }}"
name="permission_{{ permission.name }}" value="1" disabled>
{% end %}
</td> </td>
</tr> </tr>
{% end %} {% end %}
@ -158,8 +164,8 @@
</tbody> </tbody>
</table> </table>
<label for="superuser">Superuser</label> <label for="full_access">{{translate('apiKeys', 'fullAccess', data['lang'])}}</label>
<input type="checkbox" class="" id="superuser" name="superuser" value="1"> <input type="checkbox" class="" id="full_access" name="full_access" value="1">
<br /> <br />
@ -240,7 +246,7 @@
"name": formDataObject.name, "name": formDataObject.name,
"server_permissions_mask": server_permissions, "server_permissions_mask": server_permissions,
"crafty_permissions_mask": crafty_permissions, "crafty_permissions_mask": crafty_permissions,
"superuser": $("#superuser").prop('checked'), "full_access": $("#full_access").prop('checked'),
}); });
console.log(formDataJsonString); console.log(formDataJsonString);

View File

@ -1,5 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="{{ data.get('lang_page', 'en') }}" class="{{data['user_data'].get('theme', 'default')}}"
data-username="{{data['user_data'].get('username', None)}}">
<head> <head>
<!-- Required meta tags --> <!-- Required meta tags -->
@ -60,6 +61,11 @@
<b>{{ translate('error', 'hereIsTheError', data['lang']) }}: {{data['error']}}</b><br /><br /> <b>{{ translate('error', 'hereIsTheError', data['lang']) }}: {{data['error']}}</b><br /><br />
That's all the help I can give you - Godspeed That's all the help I can give you - Godspeed
<br /><br /> <br /><br />
<a class="d-inline font-weight-medium" href="/panel/dashboard"><button class="btn btn-info">{{
translate('error', 'return',
data['lang'])}}</button></a>
<br>
<br>
<a class="d-inline font-weight-medium" href="https://discord.gg/9VJPhCE"> {{ translate('error', <a class="d-inline font-weight-medium" href="https://discord.gg/9VJPhCE"> {{ translate('error',
'contact', data['lang']) }}</a> 'contact', data['lang']) }}</a>
</p> </p>

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

@ -0,0 +1,17 @@
# Generated by database migrator
import peewee
def migrate(migrator, database, **kwargs):
migrator.rename_column("api_keys", "superuser", "full_access")
"""
Write your migrations here.
"""
def rollback(migrator, database, **kwargs):
migrator.rename_column("api_keys", "full_access", "superuser")
"""
Write your rollback migrations here.
"""

View File

@ -20,6 +20,7 @@
"created": "Vytvořen", "created": "Vytvořen",
"deleteKeyConfirmation": "Chcete tento API klíč odstranit? Tuto akci nelze vrátit zpět.", "deleteKeyConfirmation": "Chcete tento API klíč odstranit? Tuto akci nelze vrátit zpět.",
"deleteKeyConfirmationTitle": "Odstranit klíč API ${keyId}?", "deleteKeyConfirmationTitle": "Odstranit klíč API ${keyId}?",
"fullAccess": "všechno",
"getToken": "Získat token", "getToken": "Získat token",
"name": "Jméno", "name": "Jméno",
"nameDesc": "Jak chcete nazvat tento token API? ", "nameDesc": "Jak chcete nazvat tento token API? ",
@ -218,6 +219,7 @@
"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 ",
"return": "vrátit se na hlavní stránku",
"serverJars1": "Server JAR api je nepřístupná. Prosím zkontrolujte", "serverJars1": "Server JAR api je nepřístupná. Prosím zkontrolujte",
"serverJars2": "pro aktualní informace.", "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: {}",

View File

@ -20,6 +20,7 @@
"created": "Erstellt", "created": "Erstellt",
"deleteKeyConfirmation": "Möchten Sie diesen API Schlüssel löschen? Diese Aktion kann nicht rückgängig gemacht werden.", "deleteKeyConfirmation": "Möchten Sie diesen API Schlüssel löschen? Diese Aktion kann nicht rückgängig gemacht werden.",
"deleteKeyConfirmationTitle": "Folgenden API Schlüssel löschen: ${keyId}?", "deleteKeyConfirmationTitle": "Folgenden API Schlüssel löschen: ${keyId}?",
"fullAccess": "Vollzugriff",
"getToken": "Schlüssel erhalten", "getToken": "Schlüssel erhalten",
"name": "Name", "name": "Name",
"nameDesc": "Wie soll der API Schlüssel genannt werden? ", "nameDesc": "Wie soll der API Schlüssel genannt werden? ",
@ -203,6 +204,7 @@
"not-downloaded": "Crafty kann die auszuführende Datei nicht finden. Ist der Download abgeschlossen? Sind die Berechtigungen für Crafty korrekt?", "not-downloaded": "Crafty kann die auszuführende Datei nicht finden. Ist der Download abgeschlossen? Sind die Berechtigungen für Crafty korrekt?",
"portReminder": "Wir haben festgestellt, dass dies das erste Mal ist, dass {} ausgeführt wurde. Stellen Sie sicher, dass Sie Port {} durch Ihren Router/Firewall weiterleiten, um den Fernzugriff aus dem Internet zu ermöglichen.", "portReminder": "Wir haben festgestellt, dass dies das erste Mal ist, dass {} ausgeführt wurde. Stellen Sie sicher, dass Sie Port {} durch Ihren Router/Firewall weiterleiten, um den Fernzugriff aus dem Internet zu ermöglichen.",
"privMsg": "und der/die/das ", "privMsg": "und der/die/das ",
"return": "Zurück zum Dashboard",
"serverJars1": "Server-JAR-API nicht erreichbar. Bitte überprüfen Sie ", "serverJars1": "Server-JAR-API nicht erreichbar. Bitte überprüfen Sie ",
"serverJars2": "um die aktuellsten Informationen zu erhalten.", "serverJars2": "um die aktuellsten Informationen zu erhalten.",
"start-error": "Der Server {} konnte wegen dem Fehlercode: {} nicht gestartet werden", "start-error": "Der Server {} konnte wegen dem Fehlercode: {} nicht gestartet werden",

View File

@ -20,6 +20,7 @@
"created": "Created", "created": "Created",
"deleteKeyConfirmation": "Do you want to delete this API key? This cannot be undone.", "deleteKeyConfirmation": "Do you want to delete this API key? This cannot be undone.",
"deleteKeyConfirmationTitle": "Remove API key ${keyId}?", "deleteKeyConfirmationTitle": "Remove API key ${keyId}?",
"fullAccess": "Full Access",
"getToken": "Get A Token", "getToken": "Get A Token",
"name": "Name", "name": "Name",
"nameDesc": "What would you like to call this API token? ", "nameDesc": "What would you like to call this API token? ",
@ -28,7 +29,6 @@
"permName": "Permission Name", "permName": "Permission Name",
"perms": "Permissions", "perms": "Permissions",
"server": "Server: ", "server": "Server: ",
"superUser": "Super User",
"yes": "Yes" "yes": "Yes"
}, },
"base": { "base": {
@ -203,6 +203,7 @@
"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": "and the ", "privMsg": "and the ",
"return": "Return to Dashboard",
"serverJars1": "Server JARs API unreachable. Please check", "serverJars1": "Server JARs API unreachable. Please check",
"serverJars2": "for the most up to date information.", "serverJars2": "for the most up to date information.",
"start-error": "Server {} failed to start with error code: {}", "start-error": "Server {} failed to start with error code: {}",

View File

@ -20,6 +20,7 @@
"created": "Creado", "created": "Creado",
"deleteKeyConfirmation": "¿Quieres eliminar esta clave de API? Esto no se puede deshacer.", "deleteKeyConfirmation": "¿Quieres eliminar esta clave de API? Esto no se puede deshacer.",
"deleteKeyConfirmationTitle": "¿Eliminar la clave API ${keyId}?", "deleteKeyConfirmationTitle": "¿Eliminar la clave API ${keyId}?",
"fullAccess": "Acceso completo",
"getToken": "Conseguir un Token", "getToken": "Conseguir un Token",
"name": "Nombre", "name": "Nombre",
"nameDesc": "¿Como te gustaría llamar a este Token de API? ", "nameDesc": "¿Como te gustaría llamar a este Token de API? ",
@ -203,6 +204,7 @@
"not-downloaded": "No podemos encontrar el archivo ejecutable. ¿Ha terminado de descargarse? ¿Están los permisos puestos como ejecutable?", "not-downloaded": "No podemos encontrar el archivo ejecutable. ¿Ha terminado de descargarse? ¿Están los permisos puestos como ejecutable?",
"portReminder": "Detectamos que es la primera vez que se inicia {}. Asegúrese de configurar el puerto {} a través de su router/firewall para hacer el servidor accesible por Internet.", "portReminder": "Detectamos que es la primera vez que se inicia {}. Asegúrese de configurar el puerto {} a través de su router/firewall para hacer el servidor accesible por Internet.",
"privMsg": "y el ", "privMsg": "y el ",
"return": "Volver al panel de control",
"serverJars1": "API de Servidor JAR no disponible. por favor, compruebe", "serverJars1": "API de Servidor JAR no disponible. por favor, compruebe",
"serverJars2": "para la información más actualizada.", "serverJars2": "para la información más actualizada.",
"start-error": "Servidor {} fallo al iniciar con código de error: {}", "start-error": "Servidor {} fallo al iniciar con código de error: {}",

View File

@ -20,6 +20,7 @@
"created": "Crée", "created": "Crée",
"deleteKeyConfirmation": "Es-tu sûr de vouloir supprimer cette clé API? Tu ne pourras plus revenir en arrière.", "deleteKeyConfirmation": "Es-tu sûr de vouloir supprimer cette clé API? Tu ne pourras plus revenir en arrière.",
"deleteKeyConfirmationTitle": "Supprimer la clé API ${keyId}?", "deleteKeyConfirmationTitle": "Supprimer la clé API ${keyId}?",
"fullAccess": "Accès Complet",
"getToken": "Obtenir un Jeton", "getToken": "Obtenir un Jeton",
"name": "Nom", "name": "Nom",
"nameDesc": "Comment appeler ce Jeton d'API ? ", "nameDesc": "Comment appeler ce Jeton d'API ? ",
@ -203,6 +204,7 @@
"not-downloaded": "Nous ne parvenons pas à trouver le fichier exécutable. A-t-il fini de Télécharger ? Les permissions permettent elles l'exécution ?", "not-downloaded": "Nous ne parvenons pas à trouver le fichier exécutable. A-t-il fini de Télécharger ? Les permissions permettent elles l'exécution ?",
"portReminder": "Nous avons détecté que c'est la première fois que {} est exécuté. Assurez-vous de transférer le port {} via votre routeur/pare-feu pour le rendre accessible à distance depuis Internet.", "portReminder": "Nous avons détecté que c'est la première fois que {} est exécuté. Assurez-vous de transférer le port {} via votre routeur/pare-feu pour le rendre accessible à distance depuis Internet.",
"privMsg": "et le ", "privMsg": "et le ",
"return": "Revenir au Tableau de Bord",
"serverJars1": "l'API Server JARs est inaccessible. Merci de vérifier", "serverJars1": "l'API Server JARs est inaccessible. Merci de vérifier",
"serverJars2": "pour les informations les plus à jour.", "serverJars2": "pour les informations les plus à jour.",
"start-error": "Le serveur {} n'a pas pu démarrer avec le code d'erreur : {}", "start-error": "Le serveur {} n'a pas pu démarrer avec le code d'erreur : {}",

View File

@ -20,6 +20,7 @@
"created": "נוצר", "created": "נוצר",
"deleteKeyConfirmation": "האם ברצונך למחוק מפתח API זה? אי אפשר לבטל את זה.", "deleteKeyConfirmation": "האם ברצונך למחוק מפתח API זה? אי אפשר לבטל את זה.",
"deleteKeyConfirmationTitle": "? ${keyId} API-להסיר את מפתח ה", "deleteKeyConfirmationTitle": "? ${keyId} API-להסיר את מפתח ה",
"fullAccess": "גישה מלאה להכל",
"getToken": "קבלת אסימון", "getToken": "קבלת אסימון",
"name": "שם", "name": "שם",
"nameDesc": "הזה API-איך תרצו לקרוא לאסימון ה", "nameDesc": "הזה API-איך תרצו לקרוא לאסימון ה",
@ -203,6 +204,7 @@
"not-downloaded": "לא הצלחנו למצוא את קובץ ההפעלה שלך. האם זה סיים להוריד? האם ההרשאות מוגדרות בשביל הפעלה?", "not-downloaded": "לא הצלחנו למצוא את קובץ ההפעלה שלך. האם זה סיים להוריד? האם ההרשאות מוגדרות בשביל הפעלה?",
"portReminder": "זיהינו שזו הפעם הראשונה ש-{} מופעל. הקפידו להעביר את היציאה {} דרך הנתב/חומת האש שלכם כדי להפוך אותה לנגישה מרחוק מהאינטרנט.", "portReminder": "זיהינו שזו הפעם הראשונה ש-{} מופעל. הקפידו להעביר את היציאה {} דרך הנתב/חומת האש שלכם כדי להפוך אותה לנגישה מרחוק מהאינטרנט.",
"privMsg": "וה", "privMsg": "וה",
"return": "חזרה לפאנל",
"serverJars1": "API של צנצנות השרת אינו נגיש. אנא בדוק", "serverJars1": "API של צנצנות השרת אינו נגיש. אנא בדוק",
"serverJars2": "למידע מעודכן ביותר.", "serverJars2": "למידע מעודכן ביותר.",
"start-error": "השרת {} לא הצליח להתחיל עם קוד שגיאה: {}", "start-error": "השרת {} לא הצליח להתחיל עם קוד שגיאה: {}",

View File

@ -20,6 +20,7 @@
"created": "Creato", "created": "Creato",
"deleteKeyConfirmation": "Vuoi cancellare questa chiave API? Non puoi tornare indietro.", "deleteKeyConfirmation": "Vuoi cancellare questa chiave API? Non puoi tornare indietro.",
"deleteKeyConfirmationTitle": "Rimuovere la chiave API ${keyId}?", "deleteKeyConfirmationTitle": "Rimuovere la chiave API ${keyId}?",
"fullAccess": " Accesso completo",
"getToken": "Prendi un Token", "getToken": "Prendi un Token",
"name": "Nome", "name": "Nome",
"nameDesc": "Come desideri chiamare questo Token API? ", "nameDesc": "Come desideri chiamare questo Token API? ",
@ -53,6 +54,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 +90,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 +112,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 +181,34 @@
} }
}, },
"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 ",
"return": "Torna alla pagina iniziale",
"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 +217,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 +236,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 +291,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 +332,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 +342,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 +374,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 +406,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 +448,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 +480,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 +535,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 +561,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 +572,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 +580,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 +595,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 +629,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 +638,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 +646,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

@ -20,6 +20,7 @@
"created": "CREATED", "created": "CREATED",
"deleteKeyConfirmation": "U SURE U WANTZ TO DELETE DIS? CAN'T UNDO!", "deleteKeyConfirmation": "U SURE U WANTZ TO DELETE DIS? CAN'T UNDO!",
"deleteKeyConfirmationTitle": "I CAN EATZ IT??? : ${keyId}?", "deleteKeyConfirmationTitle": "I CAN EATZ IT??? : ${keyId}?",
"fullAccess": "All da Doors Open",
"getToken": "GIT TOKEN", "getToken": "GIT TOKEN",
"name": "NAME", "name": "NAME",
"nameDesc": "WUT WUD U LIEK 2 CALL DIS API TOKEN? ", "nameDesc": "WUT WUD U LIEK 2 CALL DIS API TOKEN? ",
@ -203,6 +204,7 @@
"not-downloaded": "SOZ BUT I FAILDZ CAN'T SEEM TO FINDZ YOUR FISH. PLZ GIB MEZ IT. I HUNGRY.", "not-downloaded": "SOZ BUT I FAILDZ CAN'T SEEM TO FINDZ YOUR FISH. PLZ GIB MEZ IT. I HUNGRY.",
"portReminder": "WE HAS DETECTD DIS AR TEH FURST TIEM {} IZ BEAN RUN. IF U WANTS IT ACESIBLE TO NEIGHBORHOOD CATS PLZ UNLOCK CAT_FLAP, {}, THRU UR ROUTR IF U HAS NOT DUN SO.", "portReminder": "WE HAS DETECTD DIS AR TEH FURST TIEM {} IZ BEAN RUN. IF U WANTS IT ACESIBLE TO NEIGHBORHOOD CATS PLZ UNLOCK CAT_FLAP, {}, THRU UR ROUTR IF U HAS NOT DUN SO.",
"privMsg": "AND THEEZ ", "privMsg": "AND THEEZ ",
"return": "Go Bak to Dashbored",
"serverJars1": "CAN'T TALK TO SERVER JARS API. CHECKZ", "serverJars1": "CAN'T TALK TO SERVER JARS API. CHECKZ",
"serverJars2": "TO SEE NEWZ STUFFZ.", "serverJars2": "TO SEE NEWZ STUFFZ.",
"start-error": "CHAIR {} FAILD 2 START WIF OOF CODE: {}", "start-error": "CHAIR {} FAILD 2 START WIF OOF CODE: {}",

View File

@ -20,6 +20,7 @@
"created": "Izveidots", "created": "Izveidots",
"deleteKeyConfirmation": "Vai vēlies dzēst šo API atslēgu? Šo nevar atdarīt.", "deleteKeyConfirmation": "Vai vēlies dzēst šo API atslēgu? Šo nevar atdarīt.",
"deleteKeyConfirmationTitle": "Noņemt API atslēgu ${keyId}?", "deleteKeyConfirmationTitle": "Noņemt API atslēgu ${keyId}?",
"fullAccess": "Pilna piekļuve",
"getToken": "Saņemt Pilnvaru (Token)", "getToken": "Saņemt Pilnvaru (Token)",
"name": "Nosaukums", "name": "Nosaukums",
"nameDesc": "Kā jūs vēlaties nosaukt šo Pilnvaru (Token)? ", "nameDesc": "Kā jūs vēlaties nosaukt šo Pilnvaru (Token)? ",
@ -204,6 +205,7 @@
"not-downloaded": "Mēs nevaram atrast jūsu izpildāmo failu. Vai tas ir beidzis lejupielādēties? Vai tā peikļuves ir uzstādītas kā palaižamas?", "not-downloaded": "Mēs nevaram atrast jūsu izpildāmo failu. Vai tas ir beidzis lejupielādēties? Vai tā peikļuves ir uzstādītas kā palaižamas?",
"portReminder": "Mēs noteicām ka šī ir pirmā reize, kad {} ir ticis palaists. Pārliecinies izlaist portu {} cauri savam rūterim/ugunsmūrim lai padarītu šo attāli pieejamu no interneta.", "portReminder": "Mēs noteicām ka šī ir pirmā reize, kad {} ir ticis palaists. Pārliecinies izlaist portu {} cauri savam rūterim/ugunsmūrim lai padarītu šo attāli pieejamu no interneta.",
"privMsg": "un ", "privMsg": "un ",
"return": "Atgriezties uz pārskatu",
"serverJars1": "Serveru JAR API nav sasniedzams. Lūdzu pārbaudiet", "serverJars1": "Serveru JAR API nav sasniedzams. Lūdzu pārbaudiet",
"serverJars2": "priekš jaunākās informācijas.", "serverJars2": "priekš jaunākās informācijas.",
"start-error": "Serveris {} neveiskmīgi startējās ar kļūdas kodu: {}", "start-error": "Serveris {} neveiskmīgi startējās ar kļūdas kodu: {}",

View File

@ -20,6 +20,7 @@
"created": "Gecreëerd", "created": "Gecreëerd",
"deleteKeyConfirmation": "Wilt u deze API sleutel verwijderen? Dit kan niet ongedaan gemaakt worden.", "deleteKeyConfirmation": "Wilt u deze API sleutel verwijderen? Dit kan niet ongedaan gemaakt worden.",
"deleteKeyConfirmationTitle": "API sleutel verwijderen ${keyId}?", "deleteKeyConfirmationTitle": "API sleutel verwijderen ${keyId}?",
"fullAccess": "Volledige toegang",
"getToken": "Verkrijg een Token", "getToken": "Verkrijg een Token",
"name": "Naam", "name": "Naam",
"nameDesc": "Hoe wilt u dit API token noemen? ", "nameDesc": "Hoe wilt u dit API token noemen? ",
@ -203,6 +204,7 @@
"not-downloaded": "We kunnen uw uitvoerbare bestand niet vinden. Is het klaar met downloaden? Zijn de rechten ingesteld op uitvoerbaar?", "not-downloaded": "We kunnen uw uitvoerbare bestand niet vinden. Is het klaar met downloaden? Zijn de rechten ingesteld op uitvoerbaar?",
"portReminder": "We hebben ontdekt dat dit de eerste keer is dat {} wordt uitgevoerd. Zorg ervoor dat u poort {} doorstuurt via uw router/firewall om deze op afstand toegankelijk te maken vanaf het internet.", "portReminder": "We hebben ontdekt dat dit de eerste keer is dat {} wordt uitgevoerd. Zorg ervoor dat u poort {} doorstuurt via uw router/firewall om deze op afstand toegankelijk te maken vanaf het internet.",
"privMsg": "en de ", "privMsg": "en de ",
"return": "Terug naar Dashboard",
"serverJars1": "Server JARs API niet bereikbaar. Controleer alstublieft", "serverJars1": "Server JARs API niet bereikbaar. Controleer alstublieft",
"serverJars2": "voor de meest recente informatie.", "serverJars2": "voor de meest recente informatie.",
"start-error": "Server {} kan niet starten met foutcode: {}", "start-error": "Server {} kan niet starten met foutcode: {}",

View File

@ -20,6 +20,7 @@
"created": "Stworzono", "created": "Stworzono",
"deleteKeyConfirmation": "Czy chcesz usunąć ten klucz API? Nie można tego cofnąć.", "deleteKeyConfirmation": "Czy chcesz usunąć ten klucz API? Nie można tego cofnąć.",
"deleteKeyConfirmationTitle": "Usunąć Klucz API ${keyId}?", "deleteKeyConfirmationTitle": "Usunąć Klucz API ${keyId}?",
"fullAccess": "Pełny dostęp",
"getToken": "Zdobądź token", "getToken": "Zdobądź token",
"name": "Nazwa", "name": "Nazwa",
"nameDesc": "Jak chcesz nazwać ten klucz API? ", "nameDesc": "Jak chcesz nazwać ten klucz API? ",
@ -203,6 +204,7 @@
"not-downloaded": "Nie możemy znaleść twojego pliku serwera. Czy skończył się pobierać? Czy permisje są ustawione na wykonywanle?", "not-downloaded": "Nie możemy znaleść twojego pliku serwera. Czy skończył się pobierać? Czy permisje są ustawione na wykonywanle?",
"portReminder": "Zauważyliśmy że to jest pierwszy raz {} kiedy był włączony. Upewnij się że otworzyłeś port {} na swoim routerze/firewallu aby korzystać z tego poza domem.", "portReminder": "Zauważyliśmy że to jest pierwszy raz {} kiedy był włączony. Upewnij się że otworzyłeś port {} na swoim routerze/firewallu aby korzystać z tego poza domem.",
"privMsg": "i także ", "privMsg": "i także ",
"return": "Powrót do panelu",
"serverJars1": "API Server Jars jest niedostępne. Proszę sprawdź", "serverJars1": "API Server Jars jest niedostępne. Proszę sprawdź",
"serverJars2": "dla najnowzsych informacji.", "serverJars2": "dla najnowzsych informacji.",
"start-error": "Serwer {} nie mógł się odpalić z powodu: {}", "start-error": "Serwer {} nie mógł się odpalić z powodu: {}",

View File

@ -20,6 +20,7 @@
"created": "สร้างเมื่อ", "created": "สร้างเมื่อ",
"deleteKeyConfirmation": "คุณต้องการลบคีย์ API นี้หรือไม่ สิ่งนี้ไม่สามารถยกเลิกได้", "deleteKeyConfirmation": "คุณต้องการลบคีย์ API นี้หรือไม่ สิ่งนี้ไม่สามารถยกเลิกได้",
"deleteKeyConfirmationTitle": "ลบคีย์ API นี้ ${keyId} หรือไม่?", "deleteKeyConfirmationTitle": "ลบคีย์ API นี้ ${keyId} หรือไม่?",
"fullAccess": "เข้าถึงได้ทั้งหมด",
"getToken": "แสดงโทเค็น", "getToken": "แสดงโทเค็น",
"name": "ชื่อ", "name": "ชื่อ",
"nameDesc": "คุณต้องการเรียกโทเค็น API นี้ว่าอะไร ? ", "nameDesc": "คุณต้องการเรียกโทเค็น API นี้ว่าอะไร ? ",
@ -203,6 +204,7 @@
"not-downloaded": "ดูเหมือนว่าเราจะไม่พบแฟ้มกระทำการของคุณ (.jar) ตรวจสอบให้แน่ใจว่าการดาวโหลดน์เสร็จสิ้นแล้ว, การอนุญาตถูกตั้งไปยังแฟ้มกระทำการหรือไม่?", "not-downloaded": "ดูเหมือนว่าเราจะไม่พบแฟ้มกระทำการของคุณ (.jar) ตรวจสอบให้แน่ใจว่าการดาวโหลดน์เสร็จสิ้นแล้ว, การอนุญาตถูกตั้งไปยังแฟ้มกระทำการหรือไม่?",
"portReminder": "เราตรวจพบว่านี่เป็นครั้งแรกที่มีการเรียกใช้ {} ตรวจสอบให้แน่ใจว่าได้ Forward port {} ผ่านเราเตอร์/ไฟร์วอลล์ของคุณเพื่อให้สามารถเข้าถึงได้จากอินเทอร์เน็ตจากระยะไกล", "portReminder": "เราตรวจพบว่านี่เป็นครั้งแรกที่มีการเรียกใช้ {} ตรวจสอบให้แน่ใจว่าได้ Forward port {} ผ่านเราเตอร์/ไฟร์วอลล์ของคุณเพื่อให้สามารถเข้าถึงได้จากอินเทอร์เน็ตจากระยะไกล",
"privMsg": "และ ", "privMsg": "และ ",
"return": "ย้อนกลับไปยังแผงควบคุม",
"serverJars1": "ไม่สามารถเข้าถึงเซิร์ฟเวอร์ JARs API กรุณาตรวจสอบ", "serverJars1": "ไม่สามารถเข้าถึงเซิร์ฟเวอร์ JARs API กรุณาตรวจสอบ",
"serverJars2": "เพื่อข้อมูลที่ทันสมัยที่สุด", "serverJars2": "เพื่อข้อมูลที่ทันสมัยที่สุด",
"start-error": "เซิร์ฟเวอร์ {} ไม่สามารถเริ่มต้นได้เนื่องจากรหัสข้อผิดพลาด: {}", "start-error": "เซิร์ฟเวอร์ {} ไม่สามารถเริ่มต้นได้เนื่องจากรหัสข้อผิดพลาด: {}",

View File

@ -20,6 +20,7 @@
"created": "Oluşturuldu", "created": "Oluşturuldu",
"deleteKeyConfirmation": "Bu API anahtarını silmek istediğine emin misin? Bu geri alınamaz.", "deleteKeyConfirmation": "Bu API anahtarını silmek istediğine emin misin? Bu geri alınamaz.",
"deleteKeyConfirmationTitle": "${keyId} API anahtarını kaldırma işlemi.", "deleteKeyConfirmationTitle": "${keyId} API anahtarını kaldırma işlemi.",
"fullAccess": "Tam Erişim",
"getToken": "Bir Token Al", "getToken": "Bir Token Al",
"name": "Ad", "name": "Ad",
"nameDesc": "Bu API tokeninin adı ne olsun?", "nameDesc": "Bu API tokeninin adı ne olsun?",
@ -203,6 +204,7 @@
"not-downloaded": "Çalıştırılabilir dosyanızı bulamıyoruz. İndirme işlemi tamamlandı mı? İzinler çalıştırılabilir olarak ayarlandı mı?", "not-downloaded": "Çalıştırılabilir dosyanızı bulamıyoruz. İndirme işlemi tamamlandı mı? İzinler çalıştırılabilir olarak ayarlandı mı?",
"portReminder": "{} ilk kez çalıştırılıyor olduğunu tespit ettik. Bunu internetten uzaktan erişilebilir kılmak için {} bağlantı noktasını yönlendiriciniz/güvenlik duvarınız üzerinden ilettiğinizden emin olun.", "portReminder": "{} ilk kez çalıştırılıyor olduğunu tespit ettik. Bunu internetten uzaktan erişilebilir kılmak için {} bağlantı noktasını yönlendiriciniz/güvenlik duvarınız üzerinden ilettiğinizden emin olun.",
"privMsg": "ve ", "privMsg": "ve ",
"return": "Arayüze Geri Dön",
"serverJars1": "Sunucu JARs API'ına erişilemiyor.", "serverJars1": "Sunucu JARs API'ına erişilemiyor.",
"serverJars2": "en güncel bilgilere sahiptir", "serverJars2": "en güncel bilgilere sahiptir",
"start-error": "{} sunucusu başlamatılamadı. Hata kodu: {}", "start-error": "{} sunucusu başlamatılamadı. Hata kodu: {}",

View File

@ -20,6 +20,7 @@
"created": "Створений", "created": "Створений",
"deleteKeyConfirmation": "Ви дійсно бажаєте видалити API ключ? Це незворотня дія.", "deleteKeyConfirmation": "Ви дійсно бажаєте видалити API ключ? Це незворотня дія.",
"deleteKeyConfirmationTitle": "Видалення API ключ ${keyId}?", "deleteKeyConfirmationTitle": "Видалення API ключ ${keyId}?",
"fullAccess": "Повний доступ",
"getToken": "Отримати Токен", "getToken": "Отримати Токен",
"name": "Ім'я", "name": "Ім'я",
"nameDesc": "Як ви хочете назвати даний API токен?", "nameDesc": "Як ви хочете назвати даний API токен?",
@ -203,6 +204,7 @@
"not-downloaded": "Здається, ми не можемо знайти ваш виконуваний файл. Чи завершилось завантаження? Чи встановлено дозволи на виконуваний файл?", "not-downloaded": "Здається, ми не можемо знайти ваш виконуваний файл. Чи завершилось завантаження? Чи встановлено дозволи на виконуваний файл?",
"portReminder": "Ми виявили це вперше {} був запущений. Обов’язково перенаправте порт {} через ваш маршрутизатор/брандмауер, щоб зробити це доступним з Інтернету.", "portReminder": "Ми виявили це вперше {} був запущений. Обов’язково перенаправте порт {} через ваш маршрутизатор/брандмауер, щоб зробити це доступним з Інтернету.",
"privMsg": "і ", "privMsg": "і ",
"return": "Повернутись до панелі",
"serverJars1": "API сервера JAR недоступний. Будь ласка, перевірте", "serverJars1": "API сервера JAR недоступний. Будь ласка, перевірте",
"serverJars2": "для найактуальнішої інформації.", "serverJars2": "для найактуальнішої інформації.",
"start-error": "Сервер {} не запустився через помилку: {}", "start-error": "Сервер {} не запустився через помилку: {}",

View File

@ -20,6 +20,7 @@
"created": "创建时间", "created": "创建时间",
"deleteKeyConfirmation": "您想要删除这个 API 密钥吗?此操作不能撤销。", "deleteKeyConfirmation": "您想要删除这个 API 密钥吗?此操作不能撤销。",
"deleteKeyConfirmationTitle": "删除 API 密钥 ${keyId}", "deleteKeyConfirmationTitle": "删除 API 密钥 ${keyId}",
"fullAccess": "完全访问",
"getToken": "获得一个令牌", "getToken": "获得一个令牌",
"name": "名称", "name": "名称",
"nameDesc": "你想把这个 API 令牌叫做什么?", "nameDesc": "你想把这个 API 令牌叫做什么?",
@ -203,6 +204,7 @@
"not-downloaded": "我们似乎找不到您的可执行文件。它下载完成了吗?可执行文件的权限设置正确了吗?", "not-downloaded": "我们似乎找不到您的可执行文件。它下载完成了吗?可执行文件的权限设置正确了吗?",
"portReminder": "我们检测到这是你首次运行 {}。请确保从您的路由器/防火墙转发 {} 端口,以使程序可以从公网远程访问。", "portReminder": "我们检测到这是你首次运行 {}。请确保从您的路由器/防火墙转发 {} 端口,以使程序可以从公网远程访问。",
"privMsg": "以及", "privMsg": "以及",
"return": "返回仪表板",
"serverJars1": "无法访问服务器 JAR API。请检查", "serverJars1": "无法访问服务器 JAR API。请检查",
"serverJars2": "以获取最新信息。", "serverJars2": "以获取最新信息。",
"start-error": "服务器 {} 启动失败,错误代码为:{}", "start-error": "服务器 {} 启动失败,错误代码为:{}",

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.2 sonar.projectVersion=4.3.3
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/**