mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'dev' into refactor/backups
This commit is contained in:
commit
0c517868b4
15
.gitlab/scripts/linux_perms_fix.sh
Normal file
15
.gitlab/scripts/linux_perms_fix.sh
Normal file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Prompt the user for the directory path
|
||||
read -p "Enter the directory path to set permissions (/var/opt/minecraft/crafty): " directory_path
|
||||
|
||||
# Check if the script is running within a Docker container
|
||||
if [ -f "/.dockerenv" ]; then
|
||||
echo "Script is running within a Docker container. Exiting with error."
|
||||
exit 1 # Exit with an error code if running in Docker
|
||||
else
|
||||
echo "Script is not running within a Docker container. Executing permissions changes..."
|
||||
# Run the commands to set permissions
|
||||
sudo chmod 700 $(find "$directory_path" -type d)
|
||||
sudo chmod 644 $(find "$directory_path" -type f)
|
||||
fi
|
25
CHANGELOG.md
25
CHANGELOG.md
@ -1,13 +1,26 @@
|
||||
# Changelog
|
||||
## --- [4.2.4] - 2023/TBD
|
||||
### New features
|
||||
TBD
|
||||
## --- [4.3.0] - 2023/03/09
|
||||
### Breaking Changes
|
||||
- This release includes database migrations that are not revertable. Once you update to this version you will not be able to rollback to a previous version.
|
||||
- In this release, we've implemented a breaking change to enhance server identification within Crafty: instead of relying on numerical integers (1, 2, 3, etc.), Servers are now uniquely identified by their UUIDs. Please adapt your API clients accordingly.
|
||||
|
||||
### Refactor
|
||||
- Refactor remote file downloads ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/719))
|
||||
### Bug fixes
|
||||
TBD
|
||||
- Fix Bedrock cert issues ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/719))
|
||||
- Make sure default.json is read from correct location ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/714))
|
||||
- Do not allow users at server limit to clone servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/718))
|
||||
- Fix bug where you cannot get to config with unloaded server ([Commit](https://gitlab.com/crafty-controller/crafty-4/-/commit/9de08973b6bb2ddf91283c5c6b0e189ff34f7e24))
|
||||
- Fix forge install v1.20, 1.20.1 and 1.20.2 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/710))
|
||||
- Fix Sanitisation on Passwords ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/715) | [Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/725))
|
||||
- Fix `Upload Imports` on unix systems, that have a space in the root dir name ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/722))
|
||||
- Fix Bedrock downloads, add `www` to download URL ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/723))
|
||||
- Fire backup webhook 'after' backup has finished ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/727))
|
||||
### Tweaks
|
||||
TBD
|
||||
- Bump pyOpenSSL & cryptography for CVE-2024-0727, CVE-2023-50782 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/716))
|
||||
- Bump cryptography for CVE-2024-26130 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/724))
|
||||
### Lang
|
||||
TBD
|
||||
- Update `de_DE, en_EN, es_ES, fr_FR, he_IL, lol_EN, lv_LV, nl_BE pl_PL, th_TH, tr_TR, uk_UA, zh_CN` translations for `4.3.0` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/715))
|
||||
<br><br>
|
||||
|
||||
## --- [4.2.3] - 2023/02/02
|
||||
|
@ -1,5 +1,5 @@
|
||||
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
|
||||
# Crafty Controller 4.2.4
|
||||
# Crafty Controller 4.3.0
|
||||
> Python based Control Panel for your Minecraft Server
|
||||
|
||||
## What is Crafty Controller?
|
||||
|
@ -79,8 +79,8 @@ class ServersController(metaclass=Singleton):
|
||||
PeeweeException: If the server already exists
|
||||
"""
|
||||
return HelperServers.create_server(
|
||||
name,
|
||||
server_uuid,
|
||||
name,
|
||||
server_dir,
|
||||
server_command,
|
||||
server_file,
|
||||
@ -159,9 +159,9 @@ class ServersController(metaclass=Singleton):
|
||||
# Servers Methods
|
||||
# **********************************************************************************
|
||||
|
||||
def get_server_instance_by_id(self, server_id: t.Union[str, int]) -> ServerInstance:
|
||||
def get_server_instance_by_id(self, server_id: t.Union[str, str]) -> ServerInstance:
|
||||
for server in self.servers_list:
|
||||
if int(server["server_id"]) == int(server_id):
|
||||
if server["server_id"] == server_id:
|
||||
return server["server_obj"]
|
||||
|
||||
logger.warning(f"Unable to find server object for server id {server_id}")
|
||||
|
@ -52,7 +52,7 @@ class UsersController:
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"minLength": 8,
|
||||
"minLength": self.helper.minimum_password_length,
|
||||
"examples": ["crafty"],
|
||||
"title": "Password",
|
||||
},
|
||||
|
@ -1,13 +1,14 @@
|
||||
import os
|
||||
import json
|
||||
import threading
|
||||
import time
|
||||
import shutil
|
||||
import logging
|
||||
from datetime import datetime
|
||||
import requests
|
||||
|
||||
from app.classes.controllers.servers_controller import ServersController
|
||||
from app.classes.models.server_permissions import PermissionsServers
|
||||
from app.classes.shared.file_helpers import FileHelpers
|
||||
from app.classes.shared.websocket_manager import WebSocketManager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -24,6 +25,113 @@ class ServerJars:
|
||||
def get_paper_jars():
|
||||
return PAPERJARS
|
||||
|
||||
def get_paper_versions(self, project):
|
||||
"""
|
||||
Retrieves a list of versions for a specified project from the PaperMC API.
|
||||
|
||||
Parameters:
|
||||
project (str): The project name to query for available versions.
|
||||
|
||||
Returns:
|
||||
list: A list of version strings available for the project. Returns an empty
|
||||
list if the API call fails or if no versions are found.
|
||||
|
||||
This function makes a GET request to the PaperMC API to fetch available project
|
||||
versions, The versions are returned in reverse order, with the most recent
|
||||
version first.
|
||||
"""
|
||||
try:
|
||||
response = requests.get(
|
||||
f"{self.paper_base}/v2/projects/{project}/", timeout=2
|
||||
)
|
||||
response.raise_for_status()
|
||||
api_data = response.json()
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading project versions for {project}: {e}")
|
||||
return []
|
||||
|
||||
versions = api_data.get("versions", [])
|
||||
versions.reverse() # Ensure the most recent version comes first
|
||||
return versions
|
||||
|
||||
def get_paper_build(self, project, version):
|
||||
"""
|
||||
Fetches the latest build for a specified project and version from PaperMC API.
|
||||
|
||||
Parameters:
|
||||
project (str): Project name, typically a server software like 'paper'.
|
||||
version (str): Project version to fetch the build number for.
|
||||
|
||||
Returns:
|
||||
int or None: Latest build number if successful, None if not or on error.
|
||||
|
||||
This method attempts to query the PaperMC API for the latest build and
|
||||
handles exceptions by logging errors and returning None.
|
||||
"""
|
||||
try:
|
||||
response = requests.get(
|
||||
f"{self.paper_base}/v2/projects/{project}/versions/{version}/builds/",
|
||||
timeout=2,
|
||||
)
|
||||
response.raise_for_status()
|
||||
api_data = response.json()
|
||||
except Exception as e:
|
||||
logger.error(f"Error fetching build for {project} {version}: {e}")
|
||||
return None
|
||||
|
||||
builds = api_data.get("builds", [])
|
||||
return builds[-1] if builds else None
|
||||
|
||||
def get_fetch_url(self, jar, server, version):
|
||||
"""
|
||||
Constructs the URL for downloading a server JAR file based on the server type.
|
||||
|
||||
Supports two main types of server JAR sources:
|
||||
- ServerJars API for servers not in PAPERJARS.
|
||||
- Paper API for servers available through the Paper project.
|
||||
|
||||
Parameters:
|
||||
jar (str): Name of the JAR file.
|
||||
server (str): Server software name (e.g., "paper").
|
||||
version (str): Server version.
|
||||
|
||||
Returns:
|
||||
str or None: URL for downloading the JAR file, or None if URL cannot be
|
||||
constructed or an error occurs.
|
||||
"""
|
||||
try:
|
||||
# Check if the server type is not specifically handled by Paper.
|
||||
if server not in PAPERJARS:
|
||||
return f"{self.base_url}/api/fetchJar/{jar}/{server}/{version}"
|
||||
|
||||
# For Paper servers, attempt to get the build for the specified version.
|
||||
paper_build_info = self.get_paper_build(server, version)
|
||||
if paper_build_info is None:
|
||||
# Log an error or handle the case where paper_build_info is None
|
||||
logger.error(
|
||||
"Error: Unable to get build information for server:"
|
||||
f" {server}, version: {version}"
|
||||
)
|
||||
return None
|
||||
|
||||
build = paper_build_info.get("build")
|
||||
if not build:
|
||||
# Log an error or handle the case where build is None or not found
|
||||
logger.error(
|
||||
f"Error: Build number not found for server:"
|
||||
f" {server}, version: {version}"
|
||||
)
|
||||
return None
|
||||
|
||||
# Construct and return the URL for downloading the Paper server JAR.
|
||||
return (
|
||||
f"{self.paper_base}/v2/projects/{server}/versions/{version}/"
|
||||
f"builds/{build}/downloads/{server}-{version}-{build}.jar"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred while constructing fetch URL: {e}")
|
||||
return None
|
||||
|
||||
def _get_api_result(self, call_url: str):
|
||||
full_url = f"{self.base_url}{call_url}"
|
||||
|
||||
@ -44,40 +152,6 @@ class ServerJars:
|
||||
|
||||
return api_response
|
||||
|
||||
def get_paper_versions(self, project):
|
||||
try:
|
||||
response = requests.get(
|
||||
f"{self.paper_base}/v2/projects/{project}/", timeout=2
|
||||
)
|
||||
response.raise_for_status()
|
||||
api_data = json.loads(response.content)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Unable to load https://api.papermc.io/v2/projects/{project}/"
|
||||
f"api due to error: {e}"
|
||||
)
|
||||
return {}
|
||||
versions = api_data.get("versions", [])
|
||||
versions.reverse()
|
||||
return versions
|
||||
|
||||
def get_paper_build(self, project, version):
|
||||
try:
|
||||
response = requests.get(
|
||||
f"{self.paper_base}/v2/projects/{project}/versions/{version}/builds/",
|
||||
timeout=2,
|
||||
)
|
||||
response.raise_for_status()
|
||||
api_data = json.loads(response.content)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Unable to load https://api.papermc.io/v2/projects/{project}/"
|
||||
f"api due to error: {e}"
|
||||
)
|
||||
return {}
|
||||
build = api_data.get("builds", [])[-1]
|
||||
return build
|
||||
|
||||
def _read_cache(self):
|
||||
cache_file = self.helper.serverjar_cache
|
||||
cache = {}
|
||||
@ -213,55 +287,75 @@ class ServerJars:
|
||||
update_thread.start()
|
||||
|
||||
def a_download_jar(self, jar, server, version, path, server_id):
|
||||
"""
|
||||
Downloads a server JAR file and performs post-download actions including
|
||||
notifying users and setting import status.
|
||||
|
||||
This method waits for the server registration to complete, retrieves the
|
||||
download URL for the specified server JAR file.
|
||||
|
||||
Upon successful download, it either runs the installer for
|
||||
Forge servers or simply finishes the import process for other types. It
|
||||
notifies server users about the completion of the download.
|
||||
|
||||
Parameters:
|
||||
- jar (str): The name of the JAR file to download.
|
||||
- server (str): The type of server software (e.g., 'forge', 'paper').
|
||||
- version (str): The version of the server software.
|
||||
- path (str): The local filesystem path where the JAR file will be saved.
|
||||
- server_id (str): The unique identifier for the server being updated or
|
||||
imported, used for notifying users and setting the import status.
|
||||
|
||||
Returns:
|
||||
- bool: True if the JAR file was successfully downloaded and saved;
|
||||
False otherwise.
|
||||
|
||||
The method ensures that the server is properly registered before proceeding
|
||||
with the download and handles exceptions by logging errors and reverting
|
||||
the import status if necessary.
|
||||
"""
|
||||
# delaying download for server register to finish
|
||||
time.sleep(3)
|
||||
if server not in PAPERJARS:
|
||||
fetch_url = f"{self.base_url}/api/fetchJar/{jar}/{server}/{version}"
|
||||
else:
|
||||
build = self.get_paper_build(server, version).get("build", None)
|
||||
if not build:
|
||||
return
|
||||
fetch_url = (
|
||||
f"{self.paper_base}/v2/projects"
|
||||
f"/{server}/versions/{version}/builds/{build}/downloads/"
|
||||
f"{server}-{version}-{build}.jar"
|
||||
)
|
||||
|
||||
fetch_url = self.get_fetch_url(jar, server, version)
|
||||
if not fetch_url:
|
||||
return False
|
||||
|
||||
server_users = PermissionsServers.get_server_user_list(server_id)
|
||||
|
||||
# We need to make sure the server is registered before
|
||||
# we submit a db update for it's stats.
|
||||
# Make sure the server is registered before updating its stats
|
||||
while True:
|
||||
try:
|
||||
ServersController.set_import(server_id)
|
||||
for user in server_users:
|
||||
WebSocketManager().broadcast_user(user, "send_start_reload", {})
|
||||
|
||||
break
|
||||
except Exception as ex:
|
||||
logger.debug(f"server not registered yet. Delaying download - {ex}")
|
||||
logger.debug(f"Server not registered yet. Delaying download - {ex}")
|
||||
|
||||
# open a file stream
|
||||
with requests.get(fetch_url, timeout=2, stream=True) as r:
|
||||
success = False
|
||||
try:
|
||||
with open(path, "wb") as output:
|
||||
shutil.copyfileobj(r.raw, output)
|
||||
# If this is the newer forge version we will run the installer
|
||||
if server == "forge":
|
||||
ServersController.finish_import(server_id, True)
|
||||
else:
|
||||
ServersController.finish_import(server_id)
|
||||
# Initiate Download
|
||||
jar_dir = os.path.dirname(path)
|
||||
jar_name = os.path.basename(path)
|
||||
logger.info(fetch_url)
|
||||
success = FileHelpers.ssl_get_file(fetch_url, jar_dir, jar_name)
|
||||
|
||||
success = True
|
||||
except Exception as e:
|
||||
logger.error(f"Unable to save jar to {path} due to error:{e}")
|
||||
# Post-download actions
|
||||
if success:
|
||||
if server == "forge":
|
||||
# If this is the newer Forge version, run the installer
|
||||
ServersController.finish_import(server_id, True)
|
||||
else:
|
||||
ServersController.finish_import(server_id)
|
||||
server_users = PermissionsServers.get_server_user_list(server_id)
|
||||
|
||||
# Notify users
|
||||
for user in server_users:
|
||||
WebSocketManager().broadcast_user(
|
||||
user, "notification", "Executable download finished"
|
||||
)
|
||||
time.sleep(3)
|
||||
time.sleep(3) # Delay for user notification
|
||||
WebSocketManager().broadcast_user(user, "send_start_reload", {})
|
||||
return success
|
||||
else:
|
||||
logger.error(f"Unable to save jar to {path} due to download failure.")
|
||||
ServersController.finish_import(server_id)
|
||||
|
||||
return success
|
||||
|
@ -33,9 +33,9 @@ class AuditLog(BaseModel):
|
||||
user_name = CharField(default="")
|
||||
user_id = IntegerField(default=0, index=True)
|
||||
source_ip = CharField(default="127.0.0.1")
|
||||
server_id = IntegerField(
|
||||
default=None, index=True
|
||||
) # When auditing global events, use server ID 0
|
||||
server_id = ForeignKeyField(
|
||||
Servers, backref="audit_server", null=True
|
||||
) # When auditing global events, use server ID null
|
||||
log_msg = TextField(default="")
|
||||
|
||||
class Meta:
|
||||
@ -81,7 +81,7 @@ class HostStats(BaseModel):
|
||||
# **********************************************************************************
|
||||
class Webhooks(BaseModel):
|
||||
id = AutoField()
|
||||
server_id = IntegerField(null=True)
|
||||
server_id = ForeignKeyField(Servers, backref="webhook_server", null=True)
|
||||
name = CharField(default="Custom Webhook", max_length=64)
|
||||
url = CharField(default="")
|
||||
webhook_type = CharField(default="Custom")
|
||||
@ -342,7 +342,7 @@ class HelpersManagement:
|
||||
|
||||
@staticmethod
|
||||
def delete_scheduled_task_by_server(server_id):
|
||||
Schedules.delete().where(Schedules.server_id == int(server_id)).execute()
|
||||
Schedules.delete().where(Schedules.server_id == server_id).execute()
|
||||
|
||||
@staticmethod
|
||||
def get_scheduled_task(schedule_id):
|
||||
|
@ -172,9 +172,9 @@ class PermissionsServers:
|
||||
RoleServers.server_id, RoleServers.permissions
|
||||
).where(RoleServers.role_id == role_id)
|
||||
for role_server in role_servers:
|
||||
permissions_dict[
|
||||
role_server.server_id_id
|
||||
] = PermissionsServers.get_permissions(role_server.permissions)
|
||||
permissions_dict[role_server.server_id_id] = (
|
||||
PermissionsServers.get_permissions(role_server.permissions)
|
||||
)
|
||||
return permissions_dict
|
||||
|
||||
@staticmethod
|
||||
|
@ -71,7 +71,7 @@ class HelperServerStats:
|
||||
database = None
|
||||
|
||||
def __init__(self, server_id):
|
||||
self.server_id = int(server_id)
|
||||
self.server_id = server_id
|
||||
self.init_database(self.server_id)
|
||||
|
||||
def init_database(self, server_id):
|
||||
|
@ -3,7 +3,6 @@ import datetime
|
||||
import typing as t
|
||||
from peewee import (
|
||||
CharField,
|
||||
AutoField,
|
||||
DateTimeField,
|
||||
BooleanField,
|
||||
IntegerField,
|
||||
@ -13,6 +12,9 @@ from playhouse.shortcuts import model_to_dict
|
||||
from app.classes.shared.main_models import DatabaseShortcuts
|
||||
from app.classes.models.base_model import BaseModel
|
||||
|
||||
# from app.classes.models.users import Users
|
||||
from app.classes.shared.helpers import Helpers
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -20,9 +22,8 @@ logger = logging.getLogger(__name__)
|
||||
# Servers Model
|
||||
# **********************************************************************************
|
||||
class Servers(BaseModel):
|
||||
server_id = AutoField()
|
||||
server_id = CharField(primary_key=True, default=Helpers.create_uuid())
|
||||
created = DateTimeField(default=datetime.datetime.now)
|
||||
server_uuid = CharField(default="", index=True)
|
||||
server_name = CharField(default="Server", index=True)
|
||||
path = CharField(default="")
|
||||
backup_path = CharField(default="")
|
||||
@ -40,6 +41,7 @@ class Servers(BaseModel):
|
||||
type = CharField(default="minecraft-java")
|
||||
show_status = BooleanField(default=1)
|
||||
created_by = IntegerField(default=-100)
|
||||
# created_by = ForeignKeyField(Users, backref="creator_server", null=True)
|
||||
shutdown_timeout = IntegerField(default=60)
|
||||
ignored_exits = CharField(default="0")
|
||||
count_players = BooleanField(default=True)
|
||||
@ -60,8 +62,8 @@ class HelperServers:
|
||||
# **********************************************************************************
|
||||
@staticmethod
|
||||
def create_server(
|
||||
server_id: str,
|
||||
name: str,
|
||||
server_uuid: str,
|
||||
server_dir: str,
|
||||
backup_path: str,
|
||||
server_command: str,
|
||||
@ -95,25 +97,24 @@ class HelperServers:
|
||||
Raises:
|
||||
PeeweeException: If the server already exists
|
||||
"""
|
||||
return Servers.insert(
|
||||
{
|
||||
Servers.server_name: name,
|
||||
Servers.server_uuid: server_uuid,
|
||||
Servers.path: server_dir,
|
||||
Servers.executable: server_file,
|
||||
Servers.execution_command: server_command,
|
||||
Servers.auto_start: False,
|
||||
Servers.auto_start_delay: 10,
|
||||
Servers.crash_detection: False,
|
||||
Servers.log_path: server_log_file,
|
||||
Servers.server_port: server_port,
|
||||
Servers.server_ip: server_host,
|
||||
Servers.stop_command: server_stop,
|
||||
Servers.backup_path: backup_path,
|
||||
Servers.type: server_type,
|
||||
Servers.created_by: created_by,
|
||||
}
|
||||
).execute()
|
||||
return Servers.create(
|
||||
server_id=server_id,
|
||||
server_uuid=server_id,
|
||||
server_name=name,
|
||||
path=server_dir,
|
||||
executable=server_file,
|
||||
execution_command=server_command,
|
||||
auto_start=False,
|
||||
auto_start_delay=10,
|
||||
crash_detection=False,
|
||||
log_path=server_log_file,
|
||||
server_port=server_port,
|
||||
server_ip=server_host,
|
||||
stop_command=server_stop,
|
||||
backup_path=backup_path,
|
||||
type=server_type,
|
||||
created_by=created_by,
|
||||
).server_id
|
||||
|
||||
@staticmethod
|
||||
def get_server_obj(server_id):
|
||||
|
@ -18,7 +18,12 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class MainPrompt(cmd.Cmd):
|
||||
def __init__(
|
||||
self, helper, tasks_manager, migration_manager, main_controller, import3
|
||||
self,
|
||||
helper,
|
||||
tasks_manager,
|
||||
migration_manager,
|
||||
main_controller,
|
||||
import3,
|
||||
):
|
||||
super().__init__()
|
||||
self.helper: Helpers = helper
|
||||
@ -77,11 +82,11 @@ class MainPrompt(cmd.Cmd):
|
||||
# get new password from user
|
||||
new_pass = getpass.getpass(prompt=f"NEW password for: {username} > ")
|
||||
# check to make sure it fits our requirements.
|
||||
if len(new_pass) > 512:
|
||||
Console.warning("Passwords must be greater than 6char long and under 512")
|
||||
return False
|
||||
if len(new_pass) < 6:
|
||||
Console.warning("Passwords must be greater than 6char long and under 512")
|
||||
if len(new_pass) < self.helper.minimum_password_length:
|
||||
Console.warning(
|
||||
"Passwords must be greater than"
|
||||
f" {self.helper.minimum_password_length} char long"
|
||||
)
|
||||
return False
|
||||
# grab repeated password input
|
||||
new_pass_conf = getpass.getpass(prompt="Re-enter your password: > ")
|
||||
|
@ -5,6 +5,10 @@ import pathlib
|
||||
import tempfile
|
||||
import zipfile
|
||||
from zipfile import ZipFile, ZIP_DEFLATED
|
||||
import urllib.request
|
||||
import ssl
|
||||
import time
|
||||
import certifi
|
||||
|
||||
from app.classes.shared.helpers import Helpers
|
||||
from app.classes.shared.console import Console
|
||||
@ -19,6 +23,92 @@ class FileHelpers:
|
||||
def __init__(self, helper):
|
||||
self.helper: Helpers = helper
|
||||
|
||||
@staticmethod
|
||||
def ssl_get_file(
|
||||
url, out_path, out_file, max_retries=3, backoff_factor=2, headers=None
|
||||
):
|
||||
"""
|
||||
Downloads a file from a given URL using HTTPS with SSL context verification,
|
||||
retries with exponential backoff and providing download progress feedback.
|
||||
|
||||
Parameters:
|
||||
- url (str): The URL of the file to download. Must start with "https".
|
||||
- out_path (str): The local path where the file will be saved.
|
||||
- out_file (str): The name of the file to save the downloaded content as.
|
||||
- max_retries (int, optional): The maximum number of retry attempts
|
||||
in case of download failure. Defaults to 3.
|
||||
- backoff_factor (int, optional): The factor by which the wait time
|
||||
increases after each failed attempt. Defaults to 2.
|
||||
- headers (dict, optional):
|
||||
A dictionary of HTTP headers to send with the request.
|
||||
|
||||
Returns:
|
||||
- bool: True if the download was successful, False otherwise.
|
||||
|
||||
Raises:
|
||||
- urllib.error.URLError: If a URL error occurs during the download.
|
||||
- ssl.SSLError: If an SSL error occurs during the download.
|
||||
Exception: If an unexpected error occurs during the download.
|
||||
|
||||
Note:
|
||||
This method logs critical errors and download progress information.
|
||||
Ensure that the logger is properly configured to capture this information.
|
||||
"""
|
||||
if not url.lower().startswith("https"):
|
||||
logger.error("SSL File Get - Error: URL must start with https.")
|
||||
return False
|
||||
|
||||
ssl_context = ssl.create_default_context(cafile=certifi.where())
|
||||
|
||||
if not headers:
|
||||
headers = {
|
||||
"User-Agent": (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||
"Chrome/58.0.3029.110 Safari/537.3"
|
||||
)
|
||||
}
|
||||
req = urllib.request.Request(url, headers=headers)
|
||||
|
||||
write_path = os.path.join(out_path, out_file)
|
||||
attempt = 0
|
||||
|
||||
logger.info(f"SSL File Get - Requesting remote: {url}")
|
||||
file_path_full = os.path.join(out_path, out_file)
|
||||
logger.info(f"SSL File Get - Download Destination: {file_path_full}")
|
||||
|
||||
while attempt < max_retries:
|
||||
try:
|
||||
with urllib.request.urlopen(req, context=ssl_context) as response:
|
||||
total_size = response.getheader("Content-Length")
|
||||
if total_size:
|
||||
total_size = int(total_size)
|
||||
downloaded = 0
|
||||
with open(write_path, "wb") as file:
|
||||
while True:
|
||||
chunk = response.read(1024 * 1024) # 1 MB
|
||||
if not chunk:
|
||||
break
|
||||
file.write(chunk)
|
||||
downloaded += len(chunk)
|
||||
if total_size:
|
||||
progress = (downloaded / total_size) * 100
|
||||
logger.info(
|
||||
f"SSL File Get - Download progress: {progress:.2f}%"
|
||||
)
|
||||
return True
|
||||
except (urllib.error.URLError, ssl.SSLError) as e:
|
||||
logger.warning(f"SSL File Get - Attempt {attempt+1} failed: {e}")
|
||||
time.sleep(backoff_factor**attempt)
|
||||
except Exception as e:
|
||||
logger.critical(f"SSL File Get - Unexpected error: {e}")
|
||||
return False
|
||||
finally:
|
||||
attempt += 1
|
||||
|
||||
logger.error("SSL File Get - Maximum retries reached. Download failed.")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def del_dirs(path):
|
||||
path = pathlib.Path(path)
|
||||
|
@ -81,6 +81,7 @@ class Helpers:
|
||||
self.update_available = False
|
||||
self.ignored_names = ["crafty_managed.txt", "db_stats"]
|
||||
self.crafty_starting = False
|
||||
self.minimum_password_length = 8
|
||||
|
||||
@staticmethod
|
||||
def auto_installer_fix(ex):
|
||||
@ -117,7 +118,7 @@ class Helpers:
|
||||
Get latest bedrock executable url \n\n
|
||||
returns url if successful, False if not
|
||||
"""
|
||||
url = "https://minecraft.net/en-us/download/server/bedrock/"
|
||||
url = "https://www.minecraft.net/en-us/download/server/bedrock/"
|
||||
headers = {
|
||||
"Accept-Encoding": "identity",
|
||||
"Accept-Language": "en",
|
||||
@ -1112,7 +1113,7 @@ class Helpers:
|
||||
return os.path.normpath(path)
|
||||
|
||||
def find_default_password(self):
|
||||
default_file = os.path.join(self.root_dir, "default.json")
|
||||
default_file = os.path.join(self.root_dir, "app", "config", "default.json")
|
||||
data = {}
|
||||
|
||||
if Helpers.check_file_exists(default_file):
|
||||
@ -1180,25 +1181,6 @@ class Helpers:
|
||||
return temp_dir
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def download_file(executable_url, jar_path):
|
||||
try:
|
||||
response = requests.get(executable_url, timeout=5)
|
||||
except Exception as ex:
|
||||
logger.error("Could not download executable: %s", ex)
|
||||
return False
|
||||
if response.status_code != 200:
|
||||
logger.error("Unable to download file from %s", executable_url)
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(jar_path, "wb") as jar_file:
|
||||
jar_file.write(response.content)
|
||||
except Exception as e:
|
||||
logger.error("Unable to finish executable download. Error: %s", e)
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def remove_prefix(text, prefix):
|
||||
if text.startswith(prefix):
|
||||
|
@ -3,7 +3,6 @@ import time
|
||||
import shutil
|
||||
import logging
|
||||
import threading
|
||||
import urllib
|
||||
|
||||
from app.classes.controllers.server_perms_controller import PermissionsServers
|
||||
from app.classes.controllers.servers_controller import ServersController
|
||||
@ -227,25 +226,39 @@ class ImportHelpers:
|
||||
download_thread.start()
|
||||
|
||||
def download_threaded_bedrock_server(self, path, new_id):
|
||||
# downloads zip from remote url
|
||||
"""
|
||||
Downloads the latest Bedrock server, unzips it, sets necessary permissions.
|
||||
|
||||
Parameters:
|
||||
path (str): The directory path to download and unzip the Bedrock server.
|
||||
new_id (str): The identifier for the new server import operation.
|
||||
|
||||
This method handles exceptions and logs errors for each step of the process.
|
||||
"""
|
||||
try:
|
||||
bedrock_url = Helpers.get_latest_bedrock_url()
|
||||
if bedrock_url.lower().startswith("https"):
|
||||
urllib.request.urlretrieve(
|
||||
bedrock_url,
|
||||
os.path.join(path, "bedrock_server.zip"),
|
||||
if bedrock_url:
|
||||
file_path = os.path.join(path, "bedrock_server.zip")
|
||||
|
||||
success = FileHelpers.ssl_get_file(
|
||||
bedrock_url, path, "bedrock_server.zip"
|
||||
)
|
||||
if not success:
|
||||
logger.error("Failed to download the Bedrock server zip.")
|
||||
return
|
||||
|
||||
unzip_path = os.path.join(path, "bedrock_server.zip")
|
||||
unzip_path = self.helper.wtol_path(unzip_path)
|
||||
# unzips archive that was downloaded.
|
||||
FileHelpers.unzip_file(unzip_path)
|
||||
# adjusts permissions for execution if os is not windows
|
||||
if not self.helper.is_os_windows():
|
||||
os.chmod(os.path.join(path, "bedrock_server"), 0o0744)
|
||||
unzip_path = self.helper.wtol_path(file_path)
|
||||
# unzips archive that was downloaded.
|
||||
FileHelpers.unzip_file(unzip_path)
|
||||
# adjusts permissions for execution if os is not windows
|
||||
|
||||
# we'll delete the zip we downloaded now
|
||||
os.remove(os.path.join(path, "bedrock_server.zip"))
|
||||
if not self.helper.is_os_windows():
|
||||
os.chmod(os.path.join(path, "bedrock_server"), 0o0744)
|
||||
|
||||
# we'll delete the zip we downloaded now
|
||||
os.remove(file_path)
|
||||
else:
|
||||
logger.error("Bedrock download URL issue!")
|
||||
except Exception as e:
|
||||
logger.critical(
|
||||
f"Failed to download bedrock executable during server creation! \n{e}"
|
||||
|
@ -239,7 +239,7 @@ class Controller:
|
||||
try:
|
||||
os.mkdir(final_path)
|
||||
except FileExistsError:
|
||||
final_path += "_" + server["server_uuid"]
|
||||
final_path += "_" + server["server_id"]
|
||||
os.mkdir(final_path)
|
||||
try:
|
||||
FileHelpers.copy_file(
|
||||
@ -632,11 +632,11 @@ class Controller:
|
||||
# and add the user to it if he's not a superuser
|
||||
if len(captured_roles) == 0:
|
||||
if not exec_user["superuser"]:
|
||||
new_server_uuid = self.servers.get_server_data_by_id(new_server_id).get(
|
||||
"server_uuid"
|
||||
new_server_id = self.servers.get_server_data_by_id(new_server_id).get(
|
||||
"server_id"
|
||||
)
|
||||
role_id = self.roles.add_role(
|
||||
f"Creator of Server with uuid={new_server_uuid}",
|
||||
f"Creator of Server with id={new_server_id}",
|
||||
exec_user["user_id"],
|
||||
)
|
||||
self.server_perms.add_role_server(new_server_id, role_id, "11111111")
|
||||
@ -647,7 +647,7 @@ class Controller:
|
||||
role_id = role
|
||||
self.server_perms.add_role_server(new_server_id, role_id, "11111111")
|
||||
|
||||
return new_server_id, server_fs_uuid
|
||||
return new_server_id
|
||||
|
||||
@staticmethod
|
||||
def verify_jar_server(server_path: str, server_jar: str):
|
||||
@ -1095,7 +1095,7 @@ class Controller:
|
||||
for server in servers:
|
||||
server_path = server.get("path")
|
||||
new_local_server_path = os.path.join(
|
||||
new_server_path, server.get("server_uuid")
|
||||
new_server_path, server.get("server_id")
|
||||
)
|
||||
if os.path.isdir(server_path):
|
||||
WebSocketManager().broadcast_page(
|
||||
|
@ -18,13 +18,22 @@ class DatabaseBuilder:
|
||||
logger.info("Fresh Install Detected - Creating Default Settings")
|
||||
Console.info("Fresh Install Detected - Creating Default Settings")
|
||||
default_data = self.helper.find_default_password()
|
||||
if password not in default_data:
|
||||
if "password" not in default_data:
|
||||
Console.help(
|
||||
"No default password found. Using password created "
|
||||
"by Crafty. Find it in app/config/default-creds.txt"
|
||||
)
|
||||
username = default_data.get("username", "admin")
|
||||
password = default_data.get("password", password)
|
||||
if self.helper.minimum_password_length > len(
|
||||
default_data.get("password", password)
|
||||
):
|
||||
Console.critical(
|
||||
"Default password too short"
|
||||
" using Crafty's created default."
|
||||
" Find it in app/config/default-creds.txt"
|
||||
)
|
||||
else:
|
||||
password = default_data.get("password", password)
|
||||
|
||||
self.users_helper.add_user(
|
||||
username=username,
|
||||
|
@ -200,6 +200,21 @@ class Migrator(object):
|
||||
)
|
||||
return model
|
||||
|
||||
@get_model
|
||||
def alter_column_type(
|
||||
self,
|
||||
model: peewee.Model,
|
||||
column_name: str,
|
||||
field: peewee.Field,
|
||||
) -> peewee.Model:
|
||||
"""
|
||||
Alter field data type in database.
|
||||
"""
|
||||
self.operations.append(
|
||||
self.migrator.alter_column_type(model._meta.table_name, column_name, field)
|
||||
)
|
||||
return model
|
||||
|
||||
@get_model
|
||||
def rename_table(self, model: peewee.Model, new_name: str) -> peewee.Model:
|
||||
"""
|
||||
|
@ -10,7 +10,6 @@ import threading
|
||||
import logging.config
|
||||
import subprocess
|
||||
import html
|
||||
import urllib.request
|
||||
import glob
|
||||
import json
|
||||
|
||||
@ -209,7 +208,7 @@ class ServerInstance:
|
||||
self.dir_scheduler.start()
|
||||
self.start_dir_calc_task()
|
||||
self.backup_thread = threading.Thread(
|
||||
target=self.a_backup_server, daemon=True, name=f"backup_{self.name}"
|
||||
target=self.backup_server, daemon=True, name=f"backup_{self.name}"
|
||||
)
|
||||
self.is_backingup = False
|
||||
# Reset crash and update at initialization
|
||||
@ -697,6 +696,10 @@ class ServerInstance:
|
||||
version_param = version[0][0].split(".")
|
||||
version_major = int(version_param[0])
|
||||
version_minor = int(version_param[1])
|
||||
if len(version_param) > 2:
|
||||
version_sub = int(version_param[2])
|
||||
else:
|
||||
version_sub = 0
|
||||
|
||||
# Checking which version we are with
|
||||
if version_major <= 1 and version_minor < 17:
|
||||
@ -730,8 +733,8 @@ class ServerInstance:
|
||||
server_obj.execution_command = execution_command
|
||||
Console.debug(SUCCESSMSG)
|
||||
|
||||
elif version_major <= 1 and version_minor < 20:
|
||||
# NEW VERSION >= 1.17 and <= 1.20
|
||||
elif version_major <= 1 and version_minor <= 20 and version_sub < 3:
|
||||
# NEW VERSION >= 1.17 and <= 1.20.2
|
||||
# (no jar file in server dir, only run.bat and run.sh)
|
||||
|
||||
run_file_path = ""
|
||||
@ -778,7 +781,7 @@ class ServerInstance:
|
||||
server_obj.execution_command = execution_command
|
||||
Console.debug(SUCCESSMSG)
|
||||
else:
|
||||
# NEW VERSION >= 1.20
|
||||
# NEW VERSION >= 1.20.3
|
||||
# (executable jar is back in server dir)
|
||||
|
||||
# Retrieving the executable jar filename
|
||||
@ -1107,13 +1110,12 @@ class ServerInstance:
|
||||
f.write("eula=true")
|
||||
self.run_threaded_server(user_id)
|
||||
|
||||
@callback
|
||||
def backup_server(self):
|
||||
def a_backup_server(self):
|
||||
if self.settings["backup_path"] == "":
|
||||
logger.critical("Backup path is None. Canceling Backup!")
|
||||
return
|
||||
backup_thread = threading.Thread(
|
||||
target=self.a_backup_server, daemon=True, name=f"backup_{self.name}"
|
||||
target=self.backup_server, daemon=True, name=f"backup_{self.name}"
|
||||
)
|
||||
logger.info(
|
||||
f"Starting Backup Thread for server {self.settings['server_name']}."
|
||||
@ -1140,7 +1142,8 @@ class ServerInstance:
|
||||
return False
|
||||
logger.info(f"Backup Thread started for server {self.settings['server_name']}.")
|
||||
|
||||
def a_backup_server(self):
|
||||
@callback
|
||||
def backup_server(self):
|
||||
was_server_running = None
|
||||
logger.info(f"Starting server {self.name} (ID {self.server_id}) backup")
|
||||
server_users = PermissionsServers.get_server_user_list(self.server_id)
|
||||
@ -1370,7 +1373,7 @@ class ServerInstance:
|
||||
def a_jar_update(self):
|
||||
server_users = PermissionsServers.get_server_user_list(self.server_id)
|
||||
was_started = "-1"
|
||||
self.backup_server()
|
||||
self.a_backup_server()
|
||||
# checks if server is running. Calls shutdown if it is running.
|
||||
if self.check_running():
|
||||
was_started = True
|
||||
@ -1450,33 +1453,45 @@ class ServerInstance:
|
||||
|
||||
# lets download the files
|
||||
if HelperServers.get_server_type_by_id(self.server_id) != "minecraft-bedrock":
|
||||
# boolean returns true for false for success
|
||||
downloaded = Helpers.download_file(
|
||||
self.settings["executable_update_url"], current_executable
|
||||
|
||||
jar_dir = os.path.dirname(current_executable)
|
||||
jar_file_name = os.path.basename(current_executable)
|
||||
|
||||
downloaded = FileHelpers.ssl_get_file(
|
||||
self.settings["executable_update_url"], jar_dir, jar_file_name
|
||||
)
|
||||
else:
|
||||
# downloads zip from remote url
|
||||
try:
|
||||
bedrock_url = Helpers.get_latest_bedrock_url()
|
||||
if bedrock_url.lower().startswith("https"):
|
||||
urllib.request.urlretrieve(
|
||||
bedrock_url,
|
||||
os.path.join(self.settings["path"], "bedrock_server.zip"),
|
||||
if bedrock_url:
|
||||
# Use the new method for secure download
|
||||
download_path = os.path.join(
|
||||
self.settings["path"], "bedrock_server.zip"
|
||||
)
|
||||
downloaded = FileHelpers.ssl_get_file(
|
||||
bedrock_url, self.settings["path"], "bedrock_server.zip"
|
||||
)
|
||||
|
||||
unzip_path = os.path.join(self.settings["path"], "bedrock_server.zip")
|
||||
unzip_path = self.helper.wtol_path(unzip_path)
|
||||
# unzips archive that was downloaded.
|
||||
FileHelpers.unzip_file(unzip_path, server_update=True)
|
||||
# adjusts permissions for execution if os is not windows
|
||||
if not self.helper.is_os_windows():
|
||||
os.chmod(
|
||||
os.path.join(self.settings["path"], "bedrock_server"), 0o0744
|
||||
)
|
||||
if downloaded:
|
||||
unzip_path = download_path
|
||||
unzip_path = self.helper.wtol_path(unzip_path)
|
||||
|
||||
# we'll delete the zip we downloaded now
|
||||
os.remove(os.path.join(self.settings["path"], "bedrock_server.zip"))
|
||||
downloaded = True
|
||||
# unzips archive that was downloaded.
|
||||
FileHelpers.unzip_file(unzip_path, server_update=True)
|
||||
|
||||
# adjusts permissions for execution if os is not windows
|
||||
if not self.helper.is_os_windows():
|
||||
os.chmod(
|
||||
os.path.join(self.settings["path"], "bedrock_server"),
|
||||
0o0744,
|
||||
)
|
||||
|
||||
# we'll delete the zip we downloaded now
|
||||
os.remove(download_path)
|
||||
else:
|
||||
logger.error("Failed to download the Bedrock server zip.")
|
||||
downloaded = False
|
||||
except Exception as e:
|
||||
logger.critical(
|
||||
f"Failed to download bedrock executable for update \n{e}"
|
||||
|
@ -140,7 +140,7 @@ class TasksManager:
|
||||
)
|
||||
|
||||
elif command == "backup_server":
|
||||
svr.backup_server()
|
||||
svr.a_backup_server()
|
||||
|
||||
elif command == "update_executable":
|
||||
svr.jar_update()
|
||||
|
@ -174,7 +174,7 @@ class PanelHandler(BaseHandler):
|
||||
self.redirect("/panel/error?error=Invalid Server ID")
|
||||
return None
|
||||
for server in self.controller.servers.failed_servers:
|
||||
if int(server_id) == server["server_id"]:
|
||||
if server_id == server["server_id"]:
|
||||
self.failed_server = True
|
||||
return server_id
|
||||
# Does this server exist?
|
||||
@ -345,15 +345,17 @@ class PanelHandler(BaseHandler):
|
||||
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
|
||||
),
|
||||
"super_user": superuser,
|
||||
"api_key": {
|
||||
"name": api_key.name,
|
||||
"created": api_key.created,
|
||||
"server_permissions": api_key.server_permissions,
|
||||
"crafty_permissions": api_key.crafty_permissions,
|
||||
"superuser": api_key.superuser,
|
||||
}
|
||||
if api_key is not None
|
||||
else None,
|
||||
"api_key": (
|
||||
{
|
||||
"name": api_key.name,
|
||||
"created": api_key.created,
|
||||
"server_permissions": api_key.server_permissions,
|
||||
"crafty_permissions": api_key.crafty_permissions,
|
||||
"superuser": api_key.superuser,
|
||||
}
|
||||
if api_key is not None
|
||||
else None
|
||||
),
|
||||
"superuser": superuser,
|
||||
}
|
||||
try:
|
||||
@ -417,14 +419,14 @@ class PanelHandler(BaseHandler):
|
||||
self.controller.first_login = False
|
||||
if superuser: # TODO: Figure out a better solution
|
||||
try:
|
||||
page_data[
|
||||
"servers"
|
||||
] = self.controller.servers.get_all_servers_stats()
|
||||
page_data["servers"] = (
|
||||
self.controller.servers.get_all_servers_stats()
|
||||
)
|
||||
except IndexError:
|
||||
self.controller.servers.stats.record_stats()
|
||||
page_data[
|
||||
"servers"
|
||||
] = self.controller.servers.get_all_servers_stats()
|
||||
page_data["servers"] = (
|
||||
self.controller.servers.get_all_servers_stats()
|
||||
)
|
||||
else:
|
||||
try:
|
||||
user_auth = self.controller.servers.get_authorized_servers_stats(
|
||||
@ -454,19 +456,19 @@ class PanelHandler(BaseHandler):
|
||||
for server_id in user_order[:]:
|
||||
for server in un_used_servers[:]:
|
||||
if flag == 0:
|
||||
server["stats"][
|
||||
"importing"
|
||||
] = self.controller.servers.get_import_status(
|
||||
str(server["stats"]["server_id"]["server_id"])
|
||||
server["stats"]["importing"] = (
|
||||
self.controller.servers.get_import_status(
|
||||
str(server["stats"]["server_id"]["server_id"])
|
||||
)
|
||||
)
|
||||
server["stats"]["crashed"] = self.controller.servers.is_crashed(
|
||||
str(server["stats"]["server_id"]["server_id"])
|
||||
)
|
||||
try:
|
||||
server["stats"][
|
||||
"waiting_start"
|
||||
] = self.controller.servers.get_waiting_start(
|
||||
str(server["stats"]["server_id"]["server_id"])
|
||||
server["stats"]["waiting_start"] = (
|
||||
self.controller.servers.get_waiting_start(
|
||||
str(server["stats"]["server_id"]["server_id"])
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get server waiting to start: {e}")
|
||||
@ -543,9 +545,9 @@ class PanelHandler(BaseHandler):
|
||||
server_id
|
||||
)
|
||||
if not self.failed_server:
|
||||
page_data[
|
||||
"server_stats"
|
||||
] = self.controller.servers.get_server_stats_by_id(server_id)
|
||||
page_data["server_stats"] = (
|
||||
self.controller.servers.get_server_stats_by_id(server_id)
|
||||
)
|
||||
else:
|
||||
server_temp_obj = self.controller.servers.get_server_data_by_id(
|
||||
server_id
|
||||
@ -554,7 +556,7 @@ class PanelHandler(BaseHandler):
|
||||
"server_id": {
|
||||
"server_id": server_id,
|
||||
"server_name": server_temp_obj["server_name"],
|
||||
"server_uuid": server_temp_obj["server_uuid"],
|
||||
"server_uuid": server_temp_obj["server_id"],
|
||||
"path": server_temp_obj["path"],
|
||||
"log_path": server_temp_obj["log_path"],
|
||||
"executable": server_temp_obj["executable"],
|
||||
@ -572,6 +574,7 @@ class PanelHandler(BaseHandler):
|
||||
"crash_detection": server_temp_obj["crash_detection"],
|
||||
"show_status": server_temp_obj["show_status"],
|
||||
"ignored_exits": server_temp_obj["ignored_exits"],
|
||||
"count_players": server_temp_obj["count_players"],
|
||||
},
|
||||
"running": False,
|
||||
"crashed": False,
|
||||
@ -611,19 +614,19 @@ class PanelHandler(BaseHandler):
|
||||
"Config": EnumPermissionsServer.CONFIG,
|
||||
"Players": EnumPermissionsServer.PLAYERS,
|
||||
}
|
||||
page_data[
|
||||
"user_permissions"
|
||||
] = self.controller.server_perms.get_user_id_permissions_list(
|
||||
exec_user["user_id"], server_id
|
||||
page_data["user_permissions"] = (
|
||||
self.controller.server_perms.get_user_id_permissions_list(
|
||||
exec_user["user_id"], server_id
|
||||
)
|
||||
)
|
||||
if not self.failed_server:
|
||||
page_data["server_stats"][
|
||||
"crashed"
|
||||
] = self.controller.servers.is_crashed(server_id)
|
||||
page_data["server_stats"]["crashed"] = (
|
||||
self.controller.servers.is_crashed(server_id)
|
||||
)
|
||||
if not self.failed_server:
|
||||
page_data["server_stats"][
|
||||
"server_type"
|
||||
] = self.controller.servers.get_server_type_by_id(server_id)
|
||||
page_data["server_stats"]["server_type"] = (
|
||||
self.controller.servers.get_server_type_by_id(server_id)
|
||||
)
|
||||
|
||||
if not subpage:
|
||||
for spage, perm in SUBPAGE_PERMS.items():
|
||||
@ -674,23 +677,23 @@ class PanelHandler(BaseHandler):
|
||||
page_data["java_versions"] = page_java
|
||||
if subpage == "backup":
|
||||
server_info = self.controller.servers.get_server_data_by_id(server_id)
|
||||
page_data[
|
||||
"backup_config"
|
||||
] = self.controller.management.get_backup_config(server_id)
|
||||
page_data["backup_config"] = (
|
||||
self.controller.management.get_backup_config(server_id)
|
||||
)
|
||||
exclusions = []
|
||||
page_data[
|
||||
"exclusions"
|
||||
] = self.controller.management.get_excluded_backup_dirs(server_id)
|
||||
page_data[
|
||||
"backing_up"
|
||||
] = self.controller.servers.get_server_instance_by_id(
|
||||
server_id
|
||||
).is_backingup
|
||||
page_data[
|
||||
"backup_stats"
|
||||
] = self.controller.servers.get_server_instance_by_id(
|
||||
server_id
|
||||
).send_backup_status()
|
||||
page_data["exclusions"] = (
|
||||
self.controller.management.get_excluded_backup_dirs(server_id)
|
||||
)
|
||||
page_data["backing_up"] = (
|
||||
self.controller.servers.get_server_instance_by_id(
|
||||
server_id
|
||||
).is_backingup
|
||||
)
|
||||
page_data["backup_stats"] = (
|
||||
self.controller.servers.get_server_instance_by_id(
|
||||
server_id
|
||||
).send_backup_status()
|
||||
)
|
||||
# makes it so relative path is the only thing shown
|
||||
for file in page_data["exclusions"]:
|
||||
if Helpers.is_os_windows():
|
||||
@ -723,10 +726,10 @@ class PanelHandler(BaseHandler):
|
||||
server_id, hours=(days * 24)
|
||||
)
|
||||
if subpage == "webhooks":
|
||||
page_data[
|
||||
"webhooks"
|
||||
] = self.controller.management.get_webhooks_by_server(
|
||||
server_id, model=True
|
||||
page_data["webhooks"] = (
|
||||
self.controller.management.get_webhooks_by_server(
|
||||
server_id, model=True
|
||||
)
|
||||
)
|
||||
page_data["triggers"] = WebhookFactory.get_monitored_events()
|
||||
|
||||
@ -758,9 +761,9 @@ class PanelHandler(BaseHandler):
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access")
|
||||
page_data["banned_players_html"] = get_banned_players_html()
|
||||
page_data[
|
||||
"banned_players"
|
||||
] = self.controller.servers.get_banned_players(server_id)
|
||||
page_data["banned_players"] = (
|
||||
self.controller.servers.get_banned_players(server_id)
|
||||
)
|
||||
server_instance = self.controller.servers.get_server_instance_by_id(
|
||||
server_id
|
||||
)
|
||||
@ -925,9 +928,9 @@ class PanelHandler(BaseHandler):
|
||||
if item not in page_data["backgrounds"]:
|
||||
page_data["backgrounds"].append(item)
|
||||
page_data["background"] = self.controller.cached_login
|
||||
page_data[
|
||||
"login_opacity"
|
||||
] = self.controller.management.get_login_opacity()
|
||||
page_data["login_opacity"] = (
|
||||
self.controller.management.get_login_opacity()
|
||||
)
|
||||
|
||||
page_data["active_link"] = "custom_login"
|
||||
template = "panel/custom_login.html"
|
||||
@ -959,13 +962,11 @@ class PanelHandler(BaseHandler):
|
||||
page_data["servers"] = []
|
||||
page_data["servers_all"] = self.controller.servers.get_all_defined_servers()
|
||||
page_data["role-servers"] = []
|
||||
page_data[
|
||||
"permissions_all"
|
||||
] = self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||
page_data["permissions_all"] = (
|
||||
self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||
)
|
||||
page_data["permissions_list"] = set()
|
||||
page_data[
|
||||
"quantity_server"
|
||||
] = (
|
||||
page_data["quantity_server"] = (
|
||||
self.controller.crafty_perms.list_all_crafty_permissions_quantity_limits() # pylint: disable=line-too-long
|
||||
)
|
||||
page_data["languages"] = []
|
||||
@ -1007,10 +1008,10 @@ class PanelHandler(BaseHandler):
|
||||
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
|
||||
server_id
|
||||
)
|
||||
page_data[
|
||||
"user_permissions"
|
||||
] = self.controller.server_perms.get_user_id_permissions_list(
|
||||
exec_user["user_id"], server_id
|
||||
page_data["user_permissions"] = (
|
||||
self.controller.server_perms.get_user_id_permissions_list(
|
||||
exec_user["user_id"], server_id
|
||||
)
|
||||
)
|
||||
page_data["permissions"] = {
|
||||
"Commands": EnumPermissionsServer.COMMANDS,
|
||||
@ -1025,9 +1026,9 @@ class PanelHandler(BaseHandler):
|
||||
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
|
||||
server_id
|
||||
)
|
||||
page_data["server_stats"][
|
||||
"server_type"
|
||||
] = self.controller.servers.get_server_type_by_id(server_id)
|
||||
page_data["server_stats"]["server_type"] = (
|
||||
self.controller.servers.get_server_type_by_id(server_id)
|
||||
)
|
||||
page_data["new_webhook"] = True
|
||||
page_data["webhook"] = {}
|
||||
page_data["webhook"]["webhook_type"] = "Custom"
|
||||
@ -1061,10 +1062,10 @@ class PanelHandler(BaseHandler):
|
||||
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
|
||||
server_id
|
||||
)
|
||||
page_data[
|
||||
"user_permissions"
|
||||
] = self.controller.server_perms.get_user_id_permissions_list(
|
||||
exec_user["user_id"], server_id
|
||||
page_data["user_permissions"] = (
|
||||
self.controller.server_perms.get_user_id_permissions_list(
|
||||
exec_user["user_id"], server_id
|
||||
)
|
||||
)
|
||||
page_data["permissions"] = {
|
||||
"Commands": EnumPermissionsServer.COMMANDS,
|
||||
@ -1079,9 +1080,9 @@ class PanelHandler(BaseHandler):
|
||||
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
|
||||
server_id
|
||||
)
|
||||
page_data["server_stats"][
|
||||
"server_type"
|
||||
] = self.controller.servers.get_server_type_by_id(server_id)
|
||||
page_data["server_stats"]["server_type"] = (
|
||||
self.controller.servers.get_server_type_by_id(server_id)
|
||||
)
|
||||
page_data["new_webhook"] = False
|
||||
page_data["webhook"] = self.controller.management.get_webhook_by_id(
|
||||
webhook_id
|
||||
@ -1121,10 +1122,10 @@ class PanelHandler(BaseHandler):
|
||||
"Config": EnumPermissionsServer.CONFIG,
|
||||
"Players": EnumPermissionsServer.PLAYERS,
|
||||
}
|
||||
page_data[
|
||||
"user_permissions"
|
||||
] = self.controller.server_perms.get_user_id_permissions_list(
|
||||
exec_user["user_id"], server_id
|
||||
page_data["user_permissions"] = (
|
||||
self.controller.server_perms.get_user_id_permissions_list(
|
||||
exec_user["user_id"], server_id
|
||||
)
|
||||
)
|
||||
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
|
||||
server_id
|
||||
@ -1132,9 +1133,9 @@ class PanelHandler(BaseHandler):
|
||||
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
|
||||
server_id
|
||||
)
|
||||
page_data["server_stats"][
|
||||
"server_type"
|
||||
] = self.controller.servers.get_server_type_by_id(server_id)
|
||||
page_data["server_stats"]["server_type"] = (
|
||||
self.controller.servers.get_server_type_by_id(server_id)
|
||||
)
|
||||
page_data["new_schedule"] = True
|
||||
page_data["schedule"] = {}
|
||||
page_data["schedule"]["children"] = []
|
||||
@ -1189,10 +1190,10 @@ class PanelHandler(BaseHandler):
|
||||
"Config": EnumPermissionsServer.CONFIG,
|
||||
"Players": EnumPermissionsServer.PLAYERS,
|
||||
}
|
||||
page_data[
|
||||
"user_permissions"
|
||||
] = self.controller.server_perms.get_user_id_permissions_list(
|
||||
exec_user["user_id"], server_id
|
||||
page_data["user_permissions"] = (
|
||||
self.controller.server_perms.get_user_id_permissions_list(
|
||||
exec_user["user_id"], server_id
|
||||
)
|
||||
)
|
||||
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
|
||||
server_id
|
||||
@ -1200,9 +1201,9 @@ class PanelHandler(BaseHandler):
|
||||
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
|
||||
server_id
|
||||
)
|
||||
page_data["server_stats"][
|
||||
"server_type"
|
||||
] = self.controller.servers.get_server_type_by_id(server_id)
|
||||
page_data["server_stats"]["server_type"] = (
|
||||
self.controller.servers.get_server_type_by_id(server_id)
|
||||
)
|
||||
page_data["new_schedule"] = False
|
||||
page_data["schedule"] = {}
|
||||
page_data["schedule"]["server_id"] = server_id
|
||||
@ -1212,9 +1213,9 @@ class PanelHandler(BaseHandler):
|
||||
page_data["schedule"]["name"] = schedule.name
|
||||
else:
|
||||
page_data["schedule"]["name"] = ""
|
||||
page_data["schedule"][
|
||||
"children"
|
||||
] = self.controller.management.get_child_schedules(sch_id)
|
||||
page_data["schedule"]["children"] = (
|
||||
self.controller.management.get_child_schedules(sch_id)
|
||||
)
|
||||
# We check here to see if the command is any of the default ones.
|
||||
# We do not want a user changing to a custom command
|
||||
# and seeing our command there.
|
||||
@ -1280,16 +1281,16 @@ class PanelHandler(BaseHandler):
|
||||
}
|
||||
if exec_user["superuser"]:
|
||||
page_data["users"] = self.controller.users.get_all_users()
|
||||
page_data[
|
||||
"permissions_all"
|
||||
] = self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||
page_data[
|
||||
"permissions_list"
|
||||
] = self.controller.crafty_perms.get_crafty_permissions_list(user_id)
|
||||
page_data[
|
||||
"quantity_server"
|
||||
] = self.controller.crafty_perms.list_crafty_permissions_quantity_limits(
|
||||
user_id
|
||||
page_data["permissions_all"] = (
|
||||
self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||
)
|
||||
page_data["permissions_list"] = (
|
||||
self.controller.crafty_perms.get_crafty_permissions_list(user_id)
|
||||
)
|
||||
page_data["quantity_server"] = (
|
||||
self.controller.crafty_perms.list_crafty_permissions_quantity_limits(
|
||||
user_id
|
||||
)
|
||||
)
|
||||
page_data["languages"] = []
|
||||
page_data["languages"].append(
|
||||
@ -1349,12 +1350,12 @@ class PanelHandler(BaseHandler):
|
||||
page_data["user"] = self.controller.users.get_user_by_id(user_id)
|
||||
page_data["api_keys"] = self.controller.users.get_user_api_keys(user_id)
|
||||
# self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||
page_data[
|
||||
"server_permissions_all"
|
||||
] = self.controller.server_perms.list_defined_permissions()
|
||||
page_data[
|
||||
"crafty_permissions_all"
|
||||
] = self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||
page_data["server_permissions_all"] = (
|
||||
self.controller.server_perms.list_defined_permissions()
|
||||
)
|
||||
page_data["crafty_permissions_all"] = (
|
||||
self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||
)
|
||||
|
||||
if user_id is None:
|
||||
self.redirect("/panel/error?error=Invalid User ID")
|
||||
@ -1442,9 +1443,9 @@ class PanelHandler(BaseHandler):
|
||||
DatabaseShortcuts.get_data_obj(server.server_object)
|
||||
)
|
||||
page_data["servers_all"] = page_servers
|
||||
page_data[
|
||||
"permissions_all"
|
||||
] = self.controller.server_perms.list_defined_permissions()
|
||||
page_data["permissions_all"] = (
|
||||
self.controller.server_perms.list_defined_permissions()
|
||||
)
|
||||
page_data["permissions_dict"] = {}
|
||||
template = "panel/panel_edit_role.html"
|
||||
|
||||
@ -1467,12 +1468,12 @@ class PanelHandler(BaseHandler):
|
||||
DatabaseShortcuts.get_data_obj(server.server_object)
|
||||
)
|
||||
page_data["servers_all"] = page_servers
|
||||
page_data[
|
||||
"permissions_all"
|
||||
] = self.controller.server_perms.list_defined_permissions()
|
||||
page_data[
|
||||
"permissions_dict"
|
||||
] = self.controller.server_perms.get_role_permissions_dict(role_id)
|
||||
page_data["permissions_all"] = (
|
||||
self.controller.server_perms.list_defined_permissions()
|
||||
)
|
||||
page_data["permissions_dict"] = (
|
||||
self.controller.server_perms.get_role_permissions_dict(role_id)
|
||||
)
|
||||
page_data["user-roles"] = user_roles
|
||||
page_data["users"] = self.controller.users.get_all_users()
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
import logging
|
||||
import json
|
||||
import nh3
|
||||
from jsonschema import validate
|
||||
from jsonschema.exceptions import ValidationError
|
||||
|
||||
from app.classes.shared.helpers import Helpers
|
||||
from app.classes.models.users import HelperUsers
|
||||
@ -45,7 +48,7 @@ class PublicHandler(BaseHandler):
|
||||
}
|
||||
|
||||
if self.request.query:
|
||||
page_data["query"] = self.request.query
|
||||
page_data["query"] = self.request.query_arguments.get("next")[0].decode()
|
||||
|
||||
# sensible defaults
|
||||
template = "public/404.html"
|
||||
@ -75,11 +78,7 @@ class PublicHandler(BaseHandler):
|
||||
|
||||
# if we have no page, let's go to login
|
||||
else:
|
||||
if self.request.query:
|
||||
self.redirect("/login?" + self.request.query)
|
||||
else:
|
||||
self.redirect("/login")
|
||||
return
|
||||
return self.redirect("/login")
|
||||
|
||||
self.render(
|
||||
template,
|
||||
@ -89,33 +88,61 @@ class PublicHandler(BaseHandler):
|
||||
)
|
||||
|
||||
def post(self, page=None):
|
||||
# pylint: disable=no-member
|
||||
error = nh3.clean(self.get_argument("error", "Invalid Login!"))
|
||||
error_msg = nh3.clean(self.get_argument("error_msg", ""))
|
||||
# pylint: enable=no-member
|
||||
login_schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"username": {
|
||||
"type": "string",
|
||||
},
|
||||
"password": {"type": "string"},
|
||||
},
|
||||
"required": ["username", "password"],
|
||||
"additionalProperties": False,
|
||||
}
|
||||
try:
|
||||
data = json.loads(self.request.body)
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
logger.error(
|
||||
"Invalid JSON schema for API"
|
||||
f" login attempt from {self.get_remote_ip()}"
|
||||
)
|
||||
return self.finish_json(
|
||||
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
|
||||
)
|
||||
|
||||
try:
|
||||
validate(data, login_schema)
|
||||
except ValidationError as e:
|
||||
logger.error(
|
||||
"Invalid JSON schema for API"
|
||||
f" login attempt from {self.get_remote_ip()}"
|
||||
)
|
||||
return self.finish_json(
|
||||
400,
|
||||
{
|
||||
"status": "error",
|
||||
"error": "VWggb2ghIFN0aW5reS 🪠",
|
||||
"error_data": str(e),
|
||||
},
|
||||
)
|
||||
|
||||
page_data = {
|
||||
"version": self.helper.get_version_string(),
|
||||
"error": error,
|
||||
"lang": self.helper.get_setting("language"),
|
||||
"lang_page": self.helper.get_lang_page(self.helper.get_setting("language")),
|
||||
"query": "",
|
||||
}
|
||||
if self.request.query:
|
||||
page_data["query"] = self.request.query
|
||||
page_data["query"] = self.request.query_arguments.get("next")[0].decode()
|
||||
|
||||
if page == "login":
|
||||
data = json.loads(self.request.body)
|
||||
|
||||
auth_log.info(
|
||||
f"User attempting to authenticate from {self.get_remote_ip()}"
|
||||
)
|
||||
next_page = "/login"
|
||||
if self.request.query:
|
||||
next_page = "/login?" + self.request.query
|
||||
|
||||
# pylint: disable=no-member
|
||||
entered_username = nh3.clean(self.get_argument("username"))
|
||||
entered_password = self.get_argument("password")
|
||||
# pylint: enable=no-member
|
||||
entered_username = nh3.clean(data["username"]) # pylint: disable=no-member
|
||||
entered_password = data["password"]
|
||||
|
||||
try:
|
||||
user_id = HelperUsers.get_user_id_by_name(entered_username.lower())
|
||||
@ -127,16 +154,18 @@ class PublicHandler(BaseHandler):
|
||||
f" Authentication failed from remote IP {self.get_remote_ip()}"
|
||||
" Users does not exist."
|
||||
)
|
||||
error_msg = "Incorrect username or password. Please try again."
|
||||
self.finish_json(
|
||||
403,
|
||||
{
|
||||
"status": "error",
|
||||
"error": self.helper.translation.translate(
|
||||
"login", "incorrect", self.helper.get_setting("language")
|
||||
),
|
||||
},
|
||||
)
|
||||
# self.clear_cookie("user")
|
||||
# self.clear_cookie("user_data")
|
||||
self.clear_cookie("token")
|
||||
if self.request.query:
|
||||
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}")
|
||||
else:
|
||||
self.redirect(f"/login?error_msg={error_msg}")
|
||||
return
|
||||
|
||||
return self.clear_cookie("token")
|
||||
# if we don't have a user
|
||||
if not user_data:
|
||||
auth_log.error(
|
||||
@ -145,15 +174,18 @@ class PublicHandler(BaseHandler):
|
||||
" User does not exist."
|
||||
)
|
||||
self.controller.log_attempt(self.get_remote_ip(), entered_username)
|
||||
error_msg = "Incorrect username or password. Please try again."
|
||||
self.finish_json(
|
||||
403,
|
||||
{
|
||||
"status": "error",
|
||||
"error": self.helper.translation.translate(
|
||||
"login", "incorrect", self.helper.get_setting("language")
|
||||
),
|
||||
},
|
||||
)
|
||||
# self.clear_cookie("user")
|
||||
# self.clear_cookie("user_data")
|
||||
self.clear_cookie("token")
|
||||
if self.request.query:
|
||||
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}")
|
||||
else:
|
||||
self.redirect(f"/login?error_msg={error_msg}")
|
||||
return
|
||||
return self.clear_cookie("token")
|
||||
|
||||
# if they are disabled
|
||||
if not user_data.enabled:
|
||||
@ -163,19 +195,18 @@ class PublicHandler(BaseHandler):
|
||||
" User account disabled"
|
||||
)
|
||||
self.controller.log_attempt(self.get_remote_ip(), entered_username)
|
||||
error_msg = (
|
||||
"User account disabled. Please contact "
|
||||
"your system administrator for more info."
|
||||
self.finish_json(
|
||||
403,
|
||||
{
|
||||
"status": "error",
|
||||
"error": self.helper.translation.translate(
|
||||
"login", "disabled", self.helper.get_setting("language")
|
||||
),
|
||||
},
|
||||
)
|
||||
# self.clear_cookie("user")
|
||||
# self.clear_cookie("user_data")
|
||||
self.clear_cookie("token")
|
||||
if self.request.query:
|
||||
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}")
|
||||
else:
|
||||
self.redirect(f"/login?error_msg={error_msg}")
|
||||
return
|
||||
|
||||
return self.clear_cookie("token")
|
||||
login_result = self.helper.verify_pass(entered_password, user_data.password)
|
||||
|
||||
# Valid Login
|
||||
@ -200,32 +231,34 @@ class PublicHandler(BaseHandler):
|
||||
user_data.user_id, "Logged in", 0, self.get_remote_ip()
|
||||
)
|
||||
|
||||
if self.request.query_arguments.get("next"):
|
||||
next_page = self.request.query_arguments.get("next")[0].decode()
|
||||
else:
|
||||
next_page = "/panel/dashboard"
|
||||
return self.finish_json(
|
||||
200, {"status": "ok", "data": {"message": "login successful!"}}
|
||||
)
|
||||
|
||||
self.redirect(next_page)
|
||||
else:
|
||||
auth_log.error(
|
||||
f"User attempted to log into {entered_username}."
|
||||
f" Authentication failed from remote IP {self.get_remote_ip()}"
|
||||
# We'll continue on and handle unsuccessful logins
|
||||
auth_log.error(
|
||||
f"User attempted to log into {entered_username}."
|
||||
f" Authentication failed from remote IP {self.get_remote_ip()}"
|
||||
)
|
||||
self.controller.log_attempt(self.get_remote_ip(), entered_username)
|
||||
# self.clear_cookie("user")
|
||||
# self.clear_cookie("user_data")
|
||||
self.clear_cookie("token")
|
||||
error_msg = self.helper.translation.translate(
|
||||
"login", "incorrect", self.helper.get_setting("language")
|
||||
)
|
||||
if entered_password == "app/config/default-creds.txt":
|
||||
error_msg += ". "
|
||||
error_msg += self.helper.translation.translate(
|
||||
"login", "defaultPath", self.helper.get_setting("language")
|
||||
)
|
||||
self.controller.log_attempt(self.get_remote_ip(), entered_username)
|
||||
# self.clear_cookie("user")
|
||||
# self.clear_cookie("user_data")
|
||||
self.clear_cookie("token")
|
||||
error_msg = "Incorrect username or password. Please try again."
|
||||
# log this failed login attempt
|
||||
self.controller.management.add_to_audit_log(
|
||||
user_data.user_id, "Tried to log in", 0, self.get_remote_ip()
|
||||
)
|
||||
if self.request.query:
|
||||
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}")
|
||||
else:
|
||||
self.redirect(f"/login?error_msg={error_msg}")
|
||||
# log this failed login attempt
|
||||
self.controller.management.add_to_audit_log(
|
||||
user_data.user_id, "Tried to log in", 0, self.get_remote_ip()
|
||||
)
|
||||
return self.finish_json(
|
||||
403,
|
||||
{"status": "error", "error": error_msg},
|
||||
)
|
||||
else:
|
||||
if self.request.query:
|
||||
self.redirect("/login?" + self.request.query)
|
||||
else:
|
||||
self.redirect("/login")
|
||||
self.redirect("/login?")
|
||||
|
@ -208,92 +208,92 @@ def api_handlers(handler_args):
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/?",
|
||||
ApiServersServerIndexHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/backups/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/backups/?",
|
||||
ApiServersServerBackupsIndexHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/backups/backup/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/backups/backup/?",
|
||||
ApiServersServerBackupsBackupIndexHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/files/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/files/?",
|
||||
ApiServersServerFilesIndexHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/files/create/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/files/create/?",
|
||||
ApiServersServerFilesCreateHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/files/zip/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/files/zip/?",
|
||||
ApiServersServerFilesZipHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/tasks/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/tasks/?",
|
||||
ApiServersServerTasksIndexHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/tasks/([0-9]+)/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/tasks/([0-9]+)/?",
|
||||
ApiServersServerTasksTaskIndexHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/tasks/([0-9]+)/children/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/tasks/([0-9]+)/children/?",
|
||||
ApiServersServerTasksTaskChildrenHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/stats/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/stats/?",
|
||||
ApiServersServerStatsHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/history/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/history/?",
|
||||
ApiServersServerHistoryHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/webhook/([0-9]+)/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/webhook/([0-9]+)/?",
|
||||
ApiServersServerWebhooksManagementIndexHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/webhook/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/webhook/?",
|
||||
ApiServersServerWebhooksIndexHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/action/([a-z_]+)/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/action/([a-z_]+)/?",
|
||||
ApiServersServerActionHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/logs/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/logs/?",
|
||||
ApiServersServerLogsHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/users/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/users/?",
|
||||
ApiServersServerUsersHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/public/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/public/?",
|
||||
ApiServersServerPublicHandler,
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/api/v2/servers/([0-9]+)/stdin/?",
|
||||
r"/api/v2/servers/([a-z0-9-]+)/stdin/?",
|
||||
ApiServersServerStdinHandler,
|
||||
handler_args,
|
||||
),
|
||||
|
@ -17,7 +17,7 @@ login_schema = {
|
||||
"minLength": 4,
|
||||
"pattern": "^[a-z0-9_]+$",
|
||||
},
|
||||
"password": {"type": "string", "maxLength": 20, "minLength": 4},
|
||||
"password": {"type": "string", "minLength": 4},
|
||||
},
|
||||
"required": ["username", "password"],
|
||||
"additionalProperties": False,
|
||||
|
@ -80,9 +80,13 @@ class ApiCraftyConfigIndexHandler(BaseApiHandler):
|
||||
200,
|
||||
{
|
||||
"status": "ok",
|
||||
"data": self.controller.roles.get_all_role_ids()
|
||||
if get_only_ids
|
||||
else [model_to_dict(r) for r in self.controller.roles.get_all_roles()],
|
||||
"data": (
|
||||
self.controller.roles.get_all_role_ids()
|
||||
if get_only_ids
|
||||
else [
|
||||
model_to_dict(r) for r in self.controller.roles.get_all_roles()
|
||||
]
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
@ -158,9 +162,13 @@ class ApiCraftyCustomizeIndexHandler(BaseApiHandler):
|
||||
200,
|
||||
{
|
||||
"status": "ok",
|
||||
"data": self.controller.roles.get_all_role_ids()
|
||||
if get_only_ids
|
||||
else [model_to_dict(r) for r in self.controller.roles.get_all_roles()],
|
||||
"data": (
|
||||
self.controller.roles.get_all_role_ids()
|
||||
if get_only_ids
|
||||
else [
|
||||
model_to_dict(r) for r in self.controller.roles.get_all_roles()
|
||||
]
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -36,9 +36,13 @@ class ApiCraftyConfigServerDirHandler(BaseApiHandler):
|
||||
200,
|
||||
{
|
||||
"status": "ok",
|
||||
"data": self.controller.roles.get_all_role_ids()
|
||||
if get_only_ids
|
||||
else [model_to_dict(r) for r in self.controller.roles.get_all_roles()],
|
||||
"data": (
|
||||
self.controller.roles.get_all_role_ids()
|
||||
if get_only_ids
|
||||
else [
|
||||
model_to_dict(r) for r in self.controller.roles.get_all_roles()
|
||||
]
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -87,9 +87,13 @@ class ApiRolesIndexHandler(BaseApiHandler):
|
||||
200,
|
||||
{
|
||||
"status": "ok",
|
||||
"data": self.controller.roles.get_all_role_ids()
|
||||
if get_only_ids
|
||||
else [model_to_dict(r) for r in self.controller.roles.get_all_roles()],
|
||||
"data": (
|
||||
self.controller.roles.get_all_role_ids()
|
||||
if get_only_ids
|
||||
else [
|
||||
model_to_dict(r) for r in self.controller.roles.get_all_roles()
|
||||
]
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -25,8 +25,12 @@ class ApiRolesRoleServersHandler(BaseApiHandler):
|
||||
200,
|
||||
{
|
||||
"status": "ok",
|
||||
"data": PermissionsServers.get_server_ids_from_role(role_id)
|
||||
if get_only_ids
|
||||
else self.controller.roles.get_server_ids_and_perms_from_role(role_id),
|
||||
"data": (
|
||||
PermissionsServers.get_server_ids_from_role(role_id)
|
||||
if get_only_ids
|
||||
else self.controller.roles.get_server_ids_and_perms_from_role(
|
||||
role_id
|
||||
)
|
||||
),
|
||||
},
|
||||
)
|
||||
|
@ -723,9 +723,7 @@ class ApiServersIndexHandler(BaseApiHandler):
|
||||
405, {"status": "error", "error": "DATA CONSTRAINT FAILED"}
|
||||
)
|
||||
return
|
||||
new_server_id, new_server_uuid = self.controller.create_api_server(
|
||||
data, user["user_id"]
|
||||
)
|
||||
new_server_id = self.controller.create_api_server(data, user["user_id"])
|
||||
|
||||
self.controller.servers.stats.record_stats()
|
||||
|
||||
@ -734,7 +732,7 @@ class ApiServersIndexHandler(BaseApiHandler):
|
||||
(
|
||||
f"created server {data['name']}"
|
||||
f" (ID: {new_server_id})"
|
||||
f" (UUID: {new_server_uuid})"
|
||||
f" (UUID: {new_server_id})"
|
||||
),
|
||||
server_id=new_server_id,
|
||||
source_ip=self.get_remote_ip(),
|
||||
@ -746,7 +744,7 @@ class ApiServersIndexHandler(BaseApiHandler):
|
||||
"status": "ok",
|
||||
"data": {
|
||||
"new_server_id": str(new_server_id),
|
||||
"new_server_uuid": new_server_uuid,
|
||||
"new_server_uuid": new_server_id,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
@ -3,7 +3,6 @@ import os
|
||||
from app.classes.models.server_permissions import EnumPermissionsServer
|
||||
from app.classes.models.servers import Servers
|
||||
from app.classes.shared.file_helpers import FileHelpers
|
||||
from app.classes.shared.helpers import Helpers
|
||||
from app.classes.web.base_api_handler import BaseApiHandler
|
||||
|
||||
|
||||
@ -30,7 +29,15 @@ class ApiServersServerActionHandler(BaseApiHandler):
|
||||
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||
|
||||
if action == "clone_server":
|
||||
return self._clone_server(server_id, auth_data[4]["user_id"])
|
||||
if (
|
||||
self.controller.crafty_perms.can_create_server(auth_data[4]["user_id"])
|
||||
or auth_data[4]["superuser"]
|
||||
):
|
||||
self._clone_server(server_id, auth_data[4]["user_id"])
|
||||
return self.finish_json(200, {"status": "ok"})
|
||||
return self.finish_json(
|
||||
200, {"status": "error", "error": "SERVER_LIMIT_REACHED"}
|
||||
)
|
||||
if action == "eula":
|
||||
return self._agree_eula(server_id, auth_data[4]["user_id"])
|
||||
|
||||
@ -60,10 +67,20 @@ class ApiServersServerActionHandler(BaseApiHandler):
|
||||
name_counter += 1
|
||||
new_server_name = server_data.get("server_name") + f" (Copy {name_counter})"
|
||||
|
||||
new_server_uuid = Helpers.create_uuid()
|
||||
while os.path.exists(os.path.join(self.helper.servers_dir, new_server_uuid)):
|
||||
new_server_uuid = Helpers.create_uuid()
|
||||
new_server_path = os.path.join(self.helper.servers_dir, new_server_uuid)
|
||||
new_server_id = self.controller.servers.create_server(
|
||||
new_server_name,
|
||||
None,
|
||||
"",
|
||||
None,
|
||||
server_data.get("executable"),
|
||||
None,
|
||||
server_data.get("stop_command"),
|
||||
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.management.add_to_audit_log(
|
||||
user_id,
|
||||
@ -81,19 +98,19 @@ class ApiServersServerActionHandler(BaseApiHandler):
|
||||
self.helper.get_os_understandable_path(server_data.get("log_path"))
|
||||
)
|
||||
|
||||
new_server_id = self.controller.servers.create_server(
|
||||
new_server_name,
|
||||
new_server_uuid,
|
||||
new_server_path,
|
||||
"",
|
||||
new_server_command,
|
||||
server_data.get("executable"),
|
||||
new_server_log_file,
|
||||
server_data.get("stop_command"),
|
||||
server_data.get("type"),
|
||||
user_id,
|
||||
server_data.get("server_port"),
|
||||
)
|
||||
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):
|
||||
mask = self.controller.server_perms.get_permissions_mask(
|
||||
role.role_id, server_id
|
||||
)
|
||||
self.controller.server_perms.add_role_server(
|
||||
new_server_id, role.role_id, mask
|
||||
)
|
||||
|
||||
self.controller.servers.init_all_servers()
|
||||
|
||||
|
@ -145,7 +145,7 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler):
|
||||
new_server_id = new_server
|
||||
new_server = self.controller.servers.get_server_data(new_server)
|
||||
self.controller.rename_backup_dir(
|
||||
server_id, new_server_id, new_server["server_uuid"]
|
||||
server_id, new_server_id, new_server["server_id"]
|
||||
)
|
||||
# preserve current schedules
|
||||
for schedule in self.controller.management.get_schedules_by_server(
|
||||
|
@ -176,7 +176,7 @@ class ApiServersServerIndexHandler(BaseApiHandler):
|
||||
self.tasks_manager.remove_all_server_tasks(server_id)
|
||||
failed = False
|
||||
for item in self.controller.servers.failed_servers[:]:
|
||||
if item["server_id"] == int(server_id):
|
||||
if item["server_id"] == server_id:
|
||||
self.controller.servers.failed_servers.remove(item)
|
||||
failed = True
|
||||
|
||||
|
@ -17,7 +17,7 @@ def metrics_handlers(handler_args):
|
||||
handler_args,
|
||||
),
|
||||
(
|
||||
r"/metrics/servers/([0-9]+)/?",
|
||||
r"/metrics/servers/([a-z0-9-]+)/?",
|
||||
ApiOpenMetricsServersHandler,
|
||||
handler_args,
|
||||
),
|
||||
|
@ -118,15 +118,17 @@ class ServerHandler(BaseHandler):
|
||||
"lang_page": Helpers.get_lang_page(
|
||||
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
|
||||
),
|
||||
"api_key": {
|
||||
"name": api_key.name,
|
||||
"created": api_key.created,
|
||||
"server_permissions": api_key.server_permissions,
|
||||
"crafty_permissions": api_key.crafty_permissions,
|
||||
"superuser": api_key.superuser,
|
||||
}
|
||||
if api_key is not None
|
||||
else None,
|
||||
"api_key": (
|
||||
{
|
||||
"name": api_key.name,
|
||||
"created": api_key.created,
|
||||
"server_permissions": api_key.server_permissions,
|
||||
"crafty_permissions": api_key.crafty_permissions,
|
||||
"superuser": api_key.superuser,
|
||||
}
|
||||
if api_key is not None
|
||||
else None
|
||||
),
|
||||
"superuser": superuser,
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"major": 4,
|
||||
"minor": 2,
|
||||
"sub": 4
|
||||
"minor": 3,
|
||||
"sub": 0
|
||||
}
|
||||
|
@ -598,26 +598,30 @@
|
||||
</script>
|
||||
<script>
|
||||
|
||||
function send_command(server_id, command) {
|
||||
async function send_command(server_id, command) {
|
||||
/* this getCookie function is in base.html */
|
||||
const token = getCookie("_xsrf");
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: { 'X-XSRFToken': token },
|
||||
url: `/api/v2/servers/${server_id}/action/${command}`,
|
||||
success: function (data) {
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
if (command === "clone_server" && data.status === "ok") {
|
||||
window.location.reload();
|
||||
}
|
||||
/*setTimeout(function () {
|
||||
if (command != 'start_server') {
|
||||
location.reload();
|
||||
}
|
||||
}, 10000);*/
|
||||
}
|
||||
let res = await fetch(`/api/v2/servers/${server_id}/action/${command}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'token': token,
|
||||
},
|
||||
});
|
||||
let responseData = await res.json();
|
||||
if (responseData.status === "ok") {
|
||||
if (command === "clone_server"){
|
||||
window.location.reload()
|
||||
}
|
||||
console.log("Command received successfully")
|
||||
} else {
|
||||
setTimeout(function(){
|
||||
$('.modal').modal('hide');
|
||||
bootbox.alert({
|
||||
title: responseData.status,
|
||||
message: responseData.error
|
||||
});
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||
data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,7 +17,7 @@
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||
data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
@ -454,7 +454,7 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
if ($('#backup_path').val() == '') {
|
||||
console.log('true')
|
||||
|
@ -17,7 +17,7 @@
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||
data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,7 +17,7 @@
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||
data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,7 +17,7 @@
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||
data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,7 +17,7 @@
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||
data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,7 +17,7 @@
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||
data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
@ -235,7 +235,7 @@
|
||||
if (key != "start_time" && key != "cron_string" && key != "interval_type") {
|
||||
if (typeof value == "boolean") {
|
||||
return value
|
||||
}
|
||||
}
|
||||
console.log(key)
|
||||
if (key === "interval" && value === ""){
|
||||
return 0;
|
||||
@ -298,7 +298,7 @@
|
||||
title: responseData.status,
|
||||
message: responseData.error
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#schedule_form").on("submit", async function (e) {
|
||||
@ -332,7 +332,7 @@
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'X-XSRFToken': token,
|
||||
"Content-Type": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: formDataJsonString,
|
||||
});
|
||||
@ -345,7 +345,7 @@
|
||||
title: responseData.error,
|
||||
message: responseData.error_data
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -17,7 +17,7 @@
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||
data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,7 +17,7 @@
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||
data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,7 +17,7 @@
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||
data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
@ -161,7 +161,7 @@
|
||||
if (key != "start_time" && key != "cron_string" && key != "interval_type") {
|
||||
if (typeof value == "boolean") {
|
||||
return value
|
||||
}
|
||||
}
|
||||
console.log(key)
|
||||
if (key === "interval" && value === ""){
|
||||
return 0;
|
||||
@ -221,7 +221,7 @@
|
||||
title: responseData.status,
|
||||
message: responseData.error
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#webhook_form").on("submit", async function (e) {
|
||||
@ -249,7 +249,7 @@
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'X-XSRFToken': token,
|
||||
"Content-Type": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: formDataJsonString,
|
||||
});
|
||||
@ -262,15 +262,15 @@
|
||||
title: responseData.status,
|
||||
message: responseData.error
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
function hexToDiscordInt(hexColor) {
|
||||
// Remove the hash at the start if it's there
|
||||
const sanitizedHex = hexColor.startsWith('#') ? hexColor.slice(1) : hexColor;
|
||||
|
||||
|
||||
// Convert the hex to an integer
|
||||
return parseInt(sanitizedHex, 16);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||
data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -77,55 +77,49 @@
|
||||
box-shadow: 0 12px 16px 0 hsla(0, 0%, 0%, 0.4);
|
||||
}
|
||||
</style>
|
||||
{% if data['query'] %}
|
||||
<form action="/login?{{ data['query'] }}" method="post">
|
||||
{% else %}
|
||||
<form action="/login" method="post">
|
||||
{% end %}
|
||||
{% raw xsrf_form_html() %}
|
||||
<div class="form-group">
|
||||
<label class="label">{{ translate('login', 'username', data['lang']) }}</label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control login-text-input login-input"
|
||||
placeholder="{{ translate('login', 'username', data['lang']) }}" name="username" id="username"
|
||||
required="true">
|
||||
</div>
|
||||
<form id="login-form" data-query="{{ data.get('query', None) }}">
|
||||
{% raw xsrf_form_html() %}
|
||||
<div class="form-group">
|
||||
<label class="label">{{ translate('login', 'username', data['lang']) }}</label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control login-text-input login-input"
|
||||
placeholder="{{ translate('login', 'username', data['lang']) }}" name="username" id="username"
|
||||
required="true">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="label">{{ translate('login', 'password', data['lang']) }}</label>
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control login-text-input login-input"
|
||||
placeholder="{{ translate('login', 'password', data['lang']) }}" name="password" id="password"
|
||||
required="true">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="label">{{ translate('login', 'password', data['lang']) }}</label>
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control login-text-input login-input"
|
||||
placeholder="{{ translate('login', 'password', data['lang']) }}" name="password" id="password"
|
||||
required="true">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="login-input btn btn-primary submit-btn btn-block">{{ translate('login', 'login',
|
||||
data['lang']) }}</button>
|
||||
</div>
|
||||
{% if error_msg is not None %}
|
||||
<fieldset style="color: red; text-align: center;">
|
||||
<span>{{error_msg}}</span>
|
||||
</fieldset>
|
||||
{% end %}
|
||||
<div class="form-group d-flex justify-content-between">
|
||||
<div class="form-check form-check-flat mt-0">
|
||||
|
||||
</div>
|
||||
<button onclick="resetPass()" id="#resetPass" form="" class="btn btn-outline-primary btn-sm forgot-password ">{{ translate('login', 'forgotPassword',
|
||||
data['lang']) }}</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="login-input btn btn-primary submit-btn btn-block">{{ translate('login', 'login',
|
||||
data['lang']) }}</button>
|
||||
</div>
|
||||
<fieldset id="error-field" style="color: red; text-align: center;">
|
||||
</fieldset>
|
||||
<div class="form-group d-flex justify-content-between">
|
||||
<div class="form-check form-check-flat mt-0">
|
||||
|
||||
</div>
|
||||
<button onclick="resetPass()" id="#resetPass" form=""
|
||||
class="btn btn-outline-primary btn-sm forgot-password ">{{ translate('login', 'forgotPassword',
|
||||
data['lang']) }}</button>
|
||||
</div>
|
||||
|
||||
<div class="text-block text-center my-3">
|
||||
<span class="text-small font-weight-semibold"><a href="https://craftycontrol.com/">Crafty Control
|
||||
{{data['version'] }}</a> </span>
|
||||
</div>
|
||||
|
||||
<div class="text-block text-center my-3">
|
||||
<a href="/status" class="text-small forgot-password">{{ translate('login', 'viewStatus',
|
||||
data['lang']) }}</a>
|
||||
</div>
|
||||
</form>
|
||||
<div class="text-block text-center my-3">
|
||||
<span class="text-small font-weight-semibold"><a href="https://craftycontrol.com/">Crafty Control
|
||||
{{data['version'] }}</a> </span>
|
||||
</div>
|
||||
|
||||
<div class="text-block text-center my-3">
|
||||
<a href="/status" class="text-small forgot-password">{{ translate('login', 'viewStatus',
|
||||
data['lang']) }}</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
@ -155,13 +149,13 @@
|
||||
document.getElementById('login-form-background').style.background = 'rgb(34, 36, 55, ' + (opacity / 100) + ')';
|
||||
//Register Service worker for mobile app
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/static/assets/js/shared/service-worker.js', {scope: '/'})
|
||||
navigator.serviceWorker.register('/static/assets/js/shared/service-worker.js', { scope: '/' })
|
||||
.then(function (registration) {
|
||||
console.log('Service Worker Registered');
|
||||
});
|
||||
}
|
||||
});
|
||||
async function resetPass(){
|
||||
async function resetPass() {
|
||||
let res = await fetch(`/api/v2/crafty/resetPass/`, {
|
||||
method: 'GET',
|
||||
});
|
||||
@ -170,7 +164,38 @@
|
||||
bootbox.alert(responseData.data)
|
||||
|
||||
}
|
||||
$("#login-form").on("submit", async function (e) {
|
||||
e.preventDefault();
|
||||
let loginForm = document.getElementById("login-form");
|
||||
|
||||
let formData = new FormData(loginForm);
|
||||
|
||||
//Create an object from the form data entries
|
||||
let formDataObject = Object.fromEntries(formData.entries());
|
||||
console.log(formDataObject)
|
||||
let res = await fetch(`/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-XSRFToken': formDataObject._xsrf,
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"username": formDataObject.username,
|
||||
"password": formDataObject.password
|
||||
}),
|
||||
});
|
||||
let responseData = await res.json();
|
||||
if (responseData.status === "ok") {
|
||||
console.log("OK")
|
||||
if ($("#login-form").data("query")) {
|
||||
location.href = `${$("#login-form").data("query")}`;
|
||||
} else {
|
||||
location.href = `/panel/dashboard`
|
||||
}
|
||||
} else {
|
||||
$("#error-field").html(responseData.error);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
.modal-content {
|
||||
|
@ -556,7 +556,7 @@
|
||||
xmlHttpRequest.addEventListener('load', (event) => {
|
||||
if (event.target.responseText == 'success') {
|
||||
console.log('Upload for file', file.name, 'was successful!')
|
||||
document.getElementById("upload_input").innerHTML = `<div class="card-header header-sm d-flex justify-content-between align-items-center" style="width: 100%;"><input value=${fileName} type="text" id="file-uploaded" disabled></input> 🔒</div>`;
|
||||
$("#upload_input").html(`<div class="card-header header-sm d-flex justify-content-between align-items-center" style="width: 100%;"><input value="${decodeURIComponent(fileName)}" type="text" id="file-uploaded" disabled></input> 🔒</div>`);
|
||||
document.getElementById("lower_half").style.visibility = "visible";
|
||||
}
|
||||
else {
|
||||
|
@ -881,7 +881,7 @@
|
||||
xmlHttpRequest.addEventListener('load', (event) => {
|
||||
if (event.target.responseText == 'success') {
|
||||
console.log('Upload for file', file.name, 'was successful!')
|
||||
document.getElementById("upload_input").innerHTML = `<div class="card-header header-sm d-flex justify-content-between align-items-center" style="width: 100%;"><input value=${fileName} type="text" id="file-uploaded" disabled></input> 🔒</div>`;
|
||||
$("#upload_input").html(`<div class="card-header header-sm d-flex justify-content-between align-items-center" style="width: 100%;"><input value="${fileName}" type="text" id="file-uploaded" disabled></input> 🔒</div>`);
|
||||
document.getElementById("lower_half").style.visibility = "visible";
|
||||
document.getElementById("lower_half").hidden = false;
|
||||
}
|
||||
|
244
app/migrations/20240216_rework_servers_uuid.py
Normal file
244
app/migrations/20240216_rework_servers_uuid.py
Normal file
@ -0,0 +1,244 @@
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
migrator.alter_column_type(
|
||||
Servers,
|
||||
"server_id",
|
||||
peewee.CharField(primary_key=True, default=str(uuid.uuid4())),
|
||||
)
|
||||
|
||||
# Changes on Audit Log Table
|
||||
migrator.alter_column_type(
|
||||
AuditLog,
|
||||
"server_id",
|
||||
peewee.ForeignKeyField(
|
||||
Servers,
|
||||
backref="audit_server",
|
||||
null=True,
|
||||
field=peewee.CharField(primary_key=True, default=str(uuid.uuid4())),
|
||||
),
|
||||
)
|
||||
# Changes on Webhook Table
|
||||
migrator.alter_column_type(
|
||||
Webhooks,
|
||||
"server_id",
|
||||
peewee.ForeignKeyField(
|
||||
Servers,
|
||||
backref="webhook_server",
|
||||
null=True,
|
||||
field=peewee.CharField(primary_key=True, default=str(uuid.uuid4())),
|
||||
),
|
||||
)
|
||||
|
||||
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:
|
||||
logger.error("Error while migrating Data from Int to UUID (Type Change)")
|
||||
logger.error(ex)
|
||||
Console.error("Error while migrating Data from Int to UUID (Type Change)")
|
||||
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 (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
|
||||
|
||||
|
||||
def rollback(migrator: Migrator, database, **kwargs):
|
||||
"""
|
||||
Write your rollback migrations here.
|
||||
"""
|
||||
db = database
|
||||
|
||||
# Changes on Server Table
|
||||
migrator.alter_column_type(
|
||||
"servers",
|
||||
"server_id",
|
||||
peewee.AutoField(),
|
||||
)
|
||||
|
||||
# Changes on Audit Log Table
|
||||
migrator.alter_column_type(
|
||||
AuditLog,
|
||||
"server_id",
|
||||
peewee.IntegerField(default=None, index=True),
|
||||
)
|
||||
|
||||
# Changes on Webhook Table
|
||||
migrator.alter_column_type(
|
||||
Webhooks,
|
||||
"server_id",
|
||||
peewee.IntegerField(null=True),
|
||||
)
|
@ -215,7 +215,10 @@
|
||||
"version": "Version"
|
||||
},
|
||||
"login": {
|
||||
"defaultPath": "Der eingegebene Text ist der Pfad zum Passwort, nicht das Passwort selbst. Das Standartpasswort kann unter diesen Pfad eingesehen werden.",
|
||||
"disabled": "Account gesperrt. Für weitere Informationen den Serveradministrator kontaktieren",
|
||||
"forgotPassword": "Passwort vergessen",
|
||||
"incorrect": "Benutzername oder Passwort falsch",
|
||||
"login": "Einloggen",
|
||||
"password": "Passwort",
|
||||
"username": "Nutzername",
|
||||
|
@ -215,7 +215,10 @@
|
||||
"version": "Version"
|
||||
},
|
||||
"login": {
|
||||
"defaultPath": "The password you entered is the default credential path, not the password. Please find the default password in that location.",
|
||||
"disabled": "User account disabled. Please contact your system administrator for more info.",
|
||||
"forgotPassword": "Forgot Password",
|
||||
"incorrect": "Incorrect username or password",
|
||||
"login": "Log In",
|
||||
"password": "Password",
|
||||
"username": "Username",
|
||||
|
@ -111,6 +111,7 @@
|
||||
"starting": "Inicio-retrasado",
|
||||
"status": "Estado",
|
||||
"stop": "Detener",
|
||||
"storage": "Almacenamiento",
|
||||
"version": "Versión",
|
||||
"welcome": "Bienvenido a Crafty Controller"
|
||||
},
|
||||
@ -214,7 +215,10 @@
|
||||
"version": "Versión"
|
||||
},
|
||||
"login": {
|
||||
"defaultPath": "La contraseña introducida es la ruta default de las credenciales, no la contraseña. Busca la contraseña accediendo a la carpeta de la ruta",
|
||||
"disabled": "Cuenta del usuario desactivada. Porfavor contacta al administrador para mas informacion.",
|
||||
"forgotPassword": "Olvidé mi contraseña",
|
||||
"incorrect": "El nombre de usuario o contraseña es incorrecto",
|
||||
"login": "Iniciar Sesión",
|
||||
"password": "Contraseña",
|
||||
"username": "Usuario",
|
||||
@ -326,6 +330,7 @@
|
||||
"bePatientDeleteFiles": "Tenga paciencia mientras eliminamos su servidor del panel de Crafty y eliminamos todos los archivos. Esta pantalla se cerrará en unos momentos.",
|
||||
"bePatientUpdate": "Tenga paciencia mientras actualizamos el servidor. El tiempo de descarga puede variar según la velocidad del Internet...<br /> Esta pantalla se actualizará en unos momentos.",
|
||||
"cancel": "Cancelar",
|
||||
"countPlayers": "Incluir el servidor en la cuenta total de jugadores",
|
||||
"crashTime": "Tiempo de espera por crasheo",
|
||||
"crashTimeDesc": "¿Cuanto tiempo esperar para considerar el servidor como crasheado?",
|
||||
"deleteFilesQuestion": "¿Eliminar archivos del servidor del host?",
|
||||
@ -510,6 +515,7 @@
|
||||
"cpuUsage": "Uso de CPU",
|
||||
"description": "Descripción",
|
||||
"errorCalculatingUptime": "Error calculando tiempo de actividad",
|
||||
"loadingMotd": "Cargando MOTD",
|
||||
"memUsage": "Uso de memoria",
|
||||
"offline": "Desconectado",
|
||||
"online": "En línea",
|
||||
@ -577,6 +583,7 @@
|
||||
"serverUpload": "Subir servidor comprimido",
|
||||
"serverVersion": "Versión del servidor",
|
||||
"sizeInGB": "Tamaño en GB",
|
||||
"unsupported": "Versiones de Minecraft inferiores a la 1.8 no estan soportadas por Crafty. Es posible instalarlas. Resultados pueden variar.",
|
||||
"uploadButton": "Subir",
|
||||
"uploadZip": "Subir archivo Zip para importar servidor",
|
||||
"zipPath": "Ruta del servidor"
|
||||
@ -591,6 +598,15 @@
|
||||
"newServer": "Crear nuevo Servidor",
|
||||
"servers": "Servidores"
|
||||
},
|
||||
"startup": {
|
||||
"almost": "Terminando. Espera un momento...",
|
||||
"internals": "Configurando e inicializando los componentes internos de Crafty",
|
||||
"internet": "Verificando conexion a internet",
|
||||
"server": "Inicializando ",
|
||||
"serverInit": "Inicializando Servidores",
|
||||
"starting": "Crafty esta iniciando...",
|
||||
"tasks": "Iniciando el programador de tareas"
|
||||
},
|
||||
"userConfig": {
|
||||
"apiKey": "Claves API",
|
||||
"auth": "¿Autorizado? ",
|
@ -215,7 +215,10 @@
|
||||
"version": "Version"
|
||||
},
|
||||
"login": {
|
||||
"defaultPath": "Ce que tu as renseigné n'est pas le mot de passe, mais le chemin du fichier où le trouver.",
|
||||
"disabled": "Ce compte est désactivé. Merci de contacter l'administrateur de ton serveur pour plus d'informations.",
|
||||
"forgotPassword": "Mot de Passe Oublié",
|
||||
"incorrect": "Identifiant et/ou mot de passe incorrect.",
|
||||
"login": "Connexion",
|
||||
"password": "Mot de Passe",
|
||||
"username": "Nom d'Utilisateur",
|
||||
|
@ -215,7 +215,10 @@
|
||||
"version": "גרסה"
|
||||
},
|
||||
"login": {
|
||||
"defaultPath": "הסיסמה שהזנת היא נתיב האישורים המוגדר כברירת מחדל, ולא הסיסמה עצמה. אנא מצא את הסיסמה המוגדרת כברירת מחדל במיקום זה.",
|
||||
"disabled": "חשבון המשתמש מושבת. אנא פנה למנהל המערכת שלך לקבלת מידע נוסף.",
|
||||
"forgotPassword": "שכחתי סיסמה",
|
||||
"incorrect": "שם משתמש או סיסמה שגויים",
|
||||
"login": "התחברות",
|
||||
"password": "סיסמה",
|
||||
"username": "שם משתמש",
|
||||
|
@ -215,7 +215,10 @@
|
||||
"version": "VERSHUN"
|
||||
},
|
||||
"login": {
|
||||
"defaultPath": "Silleh hooman, dat iz da dafault secret path, not da passwurd. Plz find da default passwurd in dat spot.",
|
||||
"disabled": "User account no play. Plz boop ur system hooman for moar infoz.",
|
||||
"forgotPassword": "FORGWOTS YOUR SEEKRET",
|
||||
"incorrect": "U gotz wrong name or passwurd",
|
||||
"login": "WOG INZ",
|
||||
"password": "SEEKRET",
|
||||
"username": "USERNAEM",
|
||||
|
@ -216,7 +216,10 @@
|
||||
"version": "Versija"
|
||||
},
|
||||
"login": {
|
||||
"defaultPath": "Parole ko ievadijāt ir celš uz noklusētās paroles vietu, nevis noklusētā parole. Lūdzu apskatiet noklusēto paroli šajā vietā.",
|
||||
"disabled": "Lietotāja konts atspējots. Lūdzu sazinieties ar savu sistēmas administratoru priekš papildus informācijas.",
|
||||
"forgotPassword": "Aizmirsu Paroli",
|
||||
"incorrect": "Nepareizs lietotājvārds vai parole",
|
||||
"login": "Ieiet",
|
||||
"password": "Parole",
|
||||
"username": "Lietotājvārds",
|
||||
|
@ -215,7 +215,10 @@
|
||||
"version": "Versie"
|
||||
},
|
||||
"login": {
|
||||
"defaultPath": "Het ingevoerde wachtwoord is het pad naar de standaardreferentie, niet het wachtwoord zelf. Raadpleeg de standaardwachtwoord op de aangegeven locatie.",
|
||||
"disabled": "Gebruikersaccount uitgeschakeld. Neem voor meer informatie contact op met uw systeembeheerder.",
|
||||
"forgotPassword": "Wachtwoord vergeten",
|
||||
"incorrect": "Verkeerde gebruikersnaam of wachtwoord",
|
||||
"login": "Log In",
|
||||
"password": "Wachtwoord",
|
||||
"username": "gebruikersnaam",
|
||||
|
@ -215,7 +215,10 @@
|
||||
"version": "Wersja"
|
||||
},
|
||||
"login": {
|
||||
"defaultPath": "Hasło które wprowadziłeś jest podstawową ścieżką w której przechowywane są dane logowania. Znajdź podstawowe hasło w tej lokalizacji.",
|
||||
"disabled": "Konto tego użytkownika jest wyłączone. Skontaktuj się z administratorem by uzyskać więcej informacji.",
|
||||
"forgotPassword": "Zapomniałem hasła",
|
||||
"incorrect": "Niepoprawny login lub hasło/Niepoprawna nazwa użytkownika lub hasło",
|
||||
"login": "Zaloguj się",
|
||||
"password": "Hasło",
|
||||
"username": "Nazwa użytkownika",
|
||||
|
@ -215,7 +215,10 @@
|
||||
"version": "เวอร์ชั่น"
|
||||
},
|
||||
"login": {
|
||||
"defaultPath": "รหัสผ่านที่คุณกรอกคือเส้นทางข้อมูลเริ่มต้น ไม่ใช่รหัสผ่าน กรุณาค้นหารหัสผ่านเริ่มต้นในตำแหน่งนั้น",
|
||||
"disabled": "บัญชีผู้ใช้ถูกปิดใช้งาน กรุณาติดต่อผู้ดูแลระบบของคุณสำหรับข้อมูลเพิ่มเติม",
|
||||
"forgotPassword": "ลืมรหัสผ่าน",
|
||||
"incorrect": "ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง",
|
||||
"login": "เข้าสู่ระบบ",
|
||||
"password": "รหัสผ่าน",
|
||||
"username": "ชื่อผู้ใช้",
|
||||
|
@ -215,7 +215,10 @@
|
||||
"version": "Sürüm"
|
||||
},
|
||||
"login": {
|
||||
"defaultPath": "Girdiğiniz şifre varsayılan şifrenin konumudur, varsayılan şifre değil. Lütfen o konumda bulunan varsayılan şifreyi bulunuz.",
|
||||
"disabled": "Bu kullanıcı hesabı engellenmiştir. Daha fazla bilgi için lütfen sunucu yöneticiniz ile konuşunuz.",
|
||||
"forgotPassword": "Şifremi Unuttum",
|
||||
"incorrect": "Kullanıcı adınız veya şifreniz yanlış.",
|
||||
"login": "Oturum Aç",
|
||||
"password": "Şifre",
|
||||
"username": "Kullanıcı Adı",
|
||||
|
@ -85,7 +85,7 @@
|
||||
"cpuCurFreq": "Швидкість CPU",
|
||||
"cpuMaxFreq": "Максимальна швидкість CPU",
|
||||
"cpuUsage": "Використання CPU",
|
||||
"crashed": "Аварійне завершення",
|
||||
"crashed": "Краш",
|
||||
"dashboard": "Панель",
|
||||
"delay-explained": "Служба/агент нещодавно запущено та затримує запуск серверів minecraft",
|
||||
"host": "Хост",
|
||||
@ -215,7 +215,10 @@
|
||||
"version": "Версія"
|
||||
},
|
||||
"login": {
|
||||
"defaultPath": "Пароль, який ви ввели, є шляхом до облікових даних за умовчанням, а не паролем. Будь ласка, знайдіть стандартний пароль у цьому місці.",
|
||||
"disabled": "Користувача вимкнено. Зверніться до вашого системного адміністратора за допомогою.",
|
||||
"forgotPassword": "Забули пароль",
|
||||
"incorrect": "Неправильний логін або пароль",
|
||||
"login": "Вхід",
|
||||
"password": "Пароль",
|
||||
"username": "Логін",
|
||||
@ -351,7 +354,7 @@
|
||||
"sendingRequest": "Надсилання вашого запиту...",
|
||||
"serverAutoStart": "Сервер Авто-старт",
|
||||
"serverAutostartDelay": "Сервер Авто-старт затримка",
|
||||
"serverAutostartDelayDesc": "Затримка Авто-старту сервера (Якщо увімкнуто раніше)",
|
||||
"serverAutostartDelayDesc": "Затримка Авто-старту сервера (Після запуску Crafty))",
|
||||
"serverCrashDetection": "Детектор крашу сервера",
|
||||
"serverExecutable": "Виконуваний файл Серверу",
|
||||
"serverExecutableDesc": "Це виконуваний файл для запуску сервера",
|
||||
@ -369,7 +372,7 @@
|
||||
"serverPortDesc": "Цей порт призначений для статистики Crafty",
|
||||
"serverStopCommand": "Команда зупинки сервера",
|
||||
"serverStopCommandDesc": "Команда яка буде надсилатись, щоб зупинити сервер",
|
||||
"showStatus": "Показувати на публічній сторінці статус",
|
||||
"showStatus": "Показувати статус на публічній сторінці",
|
||||
"shutdownTimeout": "Час відклику зупинки",
|
||||
"statsHint1": "Цей порт на якому працює сервер. Це потрібно лиш для того щоб Crafty міг виводити статистику для цього сервера.",
|
||||
"statsHint2": "Це не змінює порт вашого сервера. Ви мусите власноруч змінити налаштування в server.properties або іншому конфігураційному файлі.",
|
||||
@ -406,7 +409,7 @@
|
||||
"logs": "Логи",
|
||||
"metrics": "Графік",
|
||||
"playerControls": "Керування Гравцями",
|
||||
"reset": "Повернутись нагору",
|
||||
"reset": "Вниз",
|
||||
"schedule": "Розклад",
|
||||
"serverDetails": "Деталі сервера",
|
||||
"terminal": "Термінал"
|
||||
|
@ -215,7 +215,10 @@
|
||||
"version": "版本"
|
||||
},
|
||||
"login": {
|
||||
"defaultPath": "您输入的密码是默认凭据的路径,不是其中的密码。请在此路径中找到默认密码。",
|
||||
"disabled": "用户账号已禁用。请联系您的系统管理员以了解更多信息。",
|
||||
"forgotPassword": "忘记密码",
|
||||
"incorrect": "用户名或密码错误",
|
||||
"login": "登录",
|
||||
"password": "密码",
|
||||
"username": "用户名",
|
||||
|
@ -4,13 +4,13 @@ argon2-cffi==23.1.0
|
||||
cached_property==1.5.2
|
||||
colorama==0.4.6
|
||||
croniter==1.4.1
|
||||
cryptography==41.0.7
|
||||
cryptography==42.0.4
|
||||
libgravatar==1.0.4
|
||||
nh3==0.2.14
|
||||
packaging==23.2
|
||||
peewee==3.13
|
||||
psutil==5.9.5
|
||||
pyOpenSSL==23.3.0
|
||||
pyOpenSSL==24.0.0
|
||||
pyjwt==2.8.0
|
||||
PyYAML==6.0.1
|
||||
requests==2.31.0
|
||||
|
@ -3,7 +3,7 @@ sonar.organization=crafty-controller
|
||||
|
||||
# This is the name and version displayed in the SonarCloud UI.
|
||||
sonar.projectName=Crafty 4
|
||||
sonar.projectVersion=4.2.4
|
||||
sonar.projectVersion=4.3.0
|
||||
sonar.python.version=3.9, 3.10, 3.11
|
||||
sonar.exclusions=app/migrations/**, app/frontend/static/assets/vendors/**
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user