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
|
# Changelog
|
||||||
## --- [4.2.4] - 2023/TBD
|
## --- [4.3.0] - 2023/03/09
|
||||||
### New features
|
### Breaking Changes
|
||||||
TBD
|
- 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
|
### 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
|
### 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
|
### 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>
|
<br><br>
|
||||||
|
|
||||||
## --- [4.2.3] - 2023/02/02
|
## --- [4.2.3] - 2023/02/02
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
|
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
|
||||||
# Crafty Controller 4.2.4
|
# Crafty Controller 4.3.0
|
||||||
> Python based Control Panel for your Minecraft Server
|
> Python based Control Panel for your Minecraft Server
|
||||||
|
|
||||||
## What is Crafty Controller?
|
## What is Crafty Controller?
|
||||||
|
@ -79,8 +79,8 @@ class ServersController(metaclass=Singleton):
|
|||||||
PeeweeException: If the server already exists
|
PeeweeException: If the server already exists
|
||||||
"""
|
"""
|
||||||
return HelperServers.create_server(
|
return HelperServers.create_server(
|
||||||
name,
|
|
||||||
server_uuid,
|
server_uuid,
|
||||||
|
name,
|
||||||
server_dir,
|
server_dir,
|
||||||
server_command,
|
server_command,
|
||||||
server_file,
|
server_file,
|
||||||
@ -159,9 +159,9 @@ class ServersController(metaclass=Singleton):
|
|||||||
# Servers Methods
|
# 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:
|
for server in self.servers_list:
|
||||||
if int(server["server_id"]) == int(server_id):
|
if server["server_id"] == server_id:
|
||||||
return server["server_obj"]
|
return server["server_obj"]
|
||||||
|
|
||||||
logger.warning(f"Unable to find server object for server id {server_id}")
|
logger.warning(f"Unable to find server object for server id {server_id}")
|
||||||
|
@ -52,7 +52,7 @@ class UsersController:
|
|||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 8,
|
"minLength": self.helper.minimum_password_length,
|
||||||
"examples": ["crafty"],
|
"examples": ["crafty"],
|
||||||
"title": "Password",
|
"title": "Password",
|
||||||
},
|
},
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
|
import os
|
||||||
import json
|
import json
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import shutil
|
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from app.classes.controllers.servers_controller import ServersController
|
from app.classes.controllers.servers_controller import ServersController
|
||||||
from app.classes.models.server_permissions import PermissionsServers
|
from app.classes.models.server_permissions import PermissionsServers
|
||||||
|
from app.classes.shared.file_helpers import FileHelpers
|
||||||
from app.classes.shared.websocket_manager import WebSocketManager
|
from app.classes.shared.websocket_manager import WebSocketManager
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -24,6 +25,113 @@ class ServerJars:
|
|||||||
def get_paper_jars():
|
def get_paper_jars():
|
||||||
return PAPERJARS
|
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):
|
def _get_api_result(self, call_url: str):
|
||||||
full_url = f"{self.base_url}{call_url}"
|
full_url = f"{self.base_url}{call_url}"
|
||||||
|
|
||||||
@ -44,40 +152,6 @@ class ServerJars:
|
|||||||
|
|
||||||
return api_response
|
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):
|
def _read_cache(self):
|
||||||
cache_file = self.helper.serverjar_cache
|
cache_file = self.helper.serverjar_cache
|
||||||
cache = {}
|
cache = {}
|
||||||
@ -213,55 +287,75 @@ class ServerJars:
|
|||||||
update_thread.start()
|
update_thread.start()
|
||||||
|
|
||||||
def a_download_jar(self, jar, server, version, path, server_id):
|
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
|
# delaying download for server register to finish
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
if server not in PAPERJARS:
|
|
||||||
fetch_url = f"{self.base_url}/api/fetchJar/{jar}/{server}/{version}"
|
fetch_url = self.get_fetch_url(jar, server, version)
|
||||||
else:
|
if not fetch_url:
|
||||||
build = self.get_paper_build(server, version).get("build", None)
|
return False
|
||||||
if not build:
|
|
||||||
return
|
|
||||||
fetch_url = (
|
|
||||||
f"{self.paper_base}/v2/projects"
|
|
||||||
f"/{server}/versions/{version}/builds/{build}/downloads/"
|
|
||||||
f"{server}-{version}-{build}.jar"
|
|
||||||
)
|
|
||||||
server_users = PermissionsServers.get_server_user_list(server_id)
|
server_users = PermissionsServers.get_server_user_list(server_id)
|
||||||
|
|
||||||
# We need to make sure the server is registered before
|
# Make sure the server is registered before updating its stats
|
||||||
# we submit a db update for it's stats.
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
ServersController.set_import(server_id)
|
ServersController.set_import(server_id)
|
||||||
for user in server_users:
|
for user in server_users:
|
||||||
WebSocketManager().broadcast_user(user, "send_start_reload", {})
|
WebSocketManager().broadcast_user(user, "send_start_reload", {})
|
||||||
|
|
||||||
break
|
break
|
||||||
except Exception as ex:
|
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
|
# Initiate Download
|
||||||
with requests.get(fetch_url, timeout=2, stream=True) as r:
|
jar_dir = os.path.dirname(path)
|
||||||
success = False
|
jar_name = os.path.basename(path)
|
||||||
try:
|
logger.info(fetch_url)
|
||||||
with open(path, "wb") as output:
|
success = FileHelpers.ssl_get_file(fetch_url, jar_dir, jar_name)
|
||||||
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)
|
|
||||||
|
|
||||||
success = True
|
# Post-download actions
|
||||||
except Exception as e:
|
if success:
|
||||||
logger.error(f"Unable to save jar to {path} due to error:{e}")
|
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)
|
ServersController.finish_import(server_id)
|
||||||
server_users = PermissionsServers.get_server_user_list(server_id)
|
|
||||||
|
|
||||||
|
# Notify users
|
||||||
for user in server_users:
|
for user in server_users:
|
||||||
WebSocketManager().broadcast_user(
|
WebSocketManager().broadcast_user(
|
||||||
user, "notification", "Executable download finished"
|
user, "notification", "Executable download finished"
|
||||||
)
|
)
|
||||||
time.sleep(3)
|
time.sleep(3) # Delay for user notification
|
||||||
WebSocketManager().broadcast_user(user, "send_start_reload", {})
|
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_name = CharField(default="")
|
||||||
user_id = IntegerField(default=0, index=True)
|
user_id = IntegerField(default=0, index=True)
|
||||||
source_ip = CharField(default="127.0.0.1")
|
source_ip = CharField(default="127.0.0.1")
|
||||||
server_id = IntegerField(
|
server_id = ForeignKeyField(
|
||||||
default=None, index=True
|
Servers, backref="audit_server", null=True
|
||||||
) # When auditing global events, use server ID 0
|
) # When auditing global events, use server ID null
|
||||||
log_msg = TextField(default="")
|
log_msg = TextField(default="")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -81,7 +81,7 @@ class HostStats(BaseModel):
|
|||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
class Webhooks(BaseModel):
|
class Webhooks(BaseModel):
|
||||||
id = AutoField()
|
id = AutoField()
|
||||||
server_id = IntegerField(null=True)
|
server_id = ForeignKeyField(Servers, backref="webhook_server", null=True)
|
||||||
name = CharField(default="Custom Webhook", max_length=64)
|
name = CharField(default="Custom Webhook", max_length=64)
|
||||||
url = CharField(default="")
|
url = CharField(default="")
|
||||||
webhook_type = CharField(default="Custom")
|
webhook_type = CharField(default="Custom")
|
||||||
@ -342,7 +342,7 @@ class HelpersManagement:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_scheduled_task_by_server(server_id):
|
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
|
@staticmethod
|
||||||
def get_scheduled_task(schedule_id):
|
def get_scheduled_task(schedule_id):
|
||||||
|
@ -172,9 +172,9 @@ class PermissionsServers:
|
|||||||
RoleServers.server_id, RoleServers.permissions
|
RoleServers.server_id, RoleServers.permissions
|
||||||
).where(RoleServers.role_id == role_id)
|
).where(RoleServers.role_id == role_id)
|
||||||
for role_server in role_servers:
|
for role_server in role_servers:
|
||||||
permissions_dict[
|
permissions_dict[role_server.server_id_id] = (
|
||||||
role_server.server_id_id
|
PermissionsServers.get_permissions(role_server.permissions)
|
||||||
] = PermissionsServers.get_permissions(role_server.permissions)
|
)
|
||||||
return permissions_dict
|
return permissions_dict
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -71,7 +71,7 @@ class HelperServerStats:
|
|||||||
database = None
|
database = None
|
||||||
|
|
||||||
def __init__(self, server_id):
|
def __init__(self, server_id):
|
||||||
self.server_id = int(server_id)
|
self.server_id = server_id
|
||||||
self.init_database(self.server_id)
|
self.init_database(self.server_id)
|
||||||
|
|
||||||
def init_database(self, server_id):
|
def init_database(self, server_id):
|
||||||
|
@ -3,7 +3,6 @@ import datetime
|
|||||||
import typing as t
|
import typing as t
|
||||||
from peewee import (
|
from peewee import (
|
||||||
CharField,
|
CharField,
|
||||||
AutoField,
|
|
||||||
DateTimeField,
|
DateTimeField,
|
||||||
BooleanField,
|
BooleanField,
|
||||||
IntegerField,
|
IntegerField,
|
||||||
@ -13,6 +12,9 @@ from playhouse.shortcuts import model_to_dict
|
|||||||
from app.classes.shared.main_models import DatabaseShortcuts
|
from app.classes.shared.main_models import DatabaseShortcuts
|
||||||
from app.classes.models.base_model import BaseModel
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -20,9 +22,8 @@ logger = logging.getLogger(__name__)
|
|||||||
# Servers Model
|
# Servers Model
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
class Servers(BaseModel):
|
class Servers(BaseModel):
|
||||||
server_id = AutoField()
|
server_id = CharField(primary_key=True, default=Helpers.create_uuid())
|
||||||
created = DateTimeField(default=datetime.datetime.now)
|
created = DateTimeField(default=datetime.datetime.now)
|
||||||
server_uuid = CharField(default="", index=True)
|
|
||||||
server_name = CharField(default="Server", index=True)
|
server_name = CharField(default="Server", index=True)
|
||||||
path = CharField(default="")
|
path = CharField(default="")
|
||||||
backup_path = CharField(default="")
|
backup_path = CharField(default="")
|
||||||
@ -40,6 +41,7 @@ class Servers(BaseModel):
|
|||||||
type = CharField(default="minecraft-java")
|
type = CharField(default="minecraft-java")
|
||||||
show_status = BooleanField(default=1)
|
show_status = BooleanField(default=1)
|
||||||
created_by = IntegerField(default=-100)
|
created_by = IntegerField(default=-100)
|
||||||
|
# created_by = ForeignKeyField(Users, backref="creator_server", null=True)
|
||||||
shutdown_timeout = IntegerField(default=60)
|
shutdown_timeout = IntegerField(default=60)
|
||||||
ignored_exits = CharField(default="0")
|
ignored_exits = CharField(default="0")
|
||||||
count_players = BooleanField(default=True)
|
count_players = BooleanField(default=True)
|
||||||
@ -60,8 +62,8 @@ class HelperServers:
|
|||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_server(
|
def create_server(
|
||||||
|
server_id: str,
|
||||||
name: str,
|
name: str,
|
||||||
server_uuid: str,
|
|
||||||
server_dir: str,
|
server_dir: str,
|
||||||
backup_path: str,
|
backup_path: str,
|
||||||
server_command: str,
|
server_command: str,
|
||||||
@ -95,25 +97,24 @@ class HelperServers:
|
|||||||
Raises:
|
Raises:
|
||||||
PeeweeException: If the server already exists
|
PeeweeException: If the server already exists
|
||||||
"""
|
"""
|
||||||
return Servers.insert(
|
return Servers.create(
|
||||||
{
|
server_id=server_id,
|
||||||
Servers.server_name: name,
|
server_uuid=server_id,
|
||||||
Servers.server_uuid: server_uuid,
|
server_name=name,
|
||||||
Servers.path: server_dir,
|
path=server_dir,
|
||||||
Servers.executable: server_file,
|
executable=server_file,
|
||||||
Servers.execution_command: server_command,
|
execution_command=server_command,
|
||||||
Servers.auto_start: False,
|
auto_start=False,
|
||||||
Servers.auto_start_delay: 10,
|
auto_start_delay=10,
|
||||||
Servers.crash_detection: False,
|
crash_detection=False,
|
||||||
Servers.log_path: server_log_file,
|
log_path=server_log_file,
|
||||||
Servers.server_port: server_port,
|
server_port=server_port,
|
||||||
Servers.server_ip: server_host,
|
server_ip=server_host,
|
||||||
Servers.stop_command: server_stop,
|
stop_command=server_stop,
|
||||||
Servers.backup_path: backup_path,
|
backup_path=backup_path,
|
||||||
Servers.type: server_type,
|
type=server_type,
|
||||||
Servers.created_by: created_by,
|
created_by=created_by,
|
||||||
}
|
).server_id
|
||||||
).execute()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_server_obj(server_id):
|
def get_server_obj(server_id):
|
||||||
|
@ -18,7 +18,12 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class MainPrompt(cmd.Cmd):
|
class MainPrompt(cmd.Cmd):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, helper, tasks_manager, migration_manager, main_controller, import3
|
self,
|
||||||
|
helper,
|
||||||
|
tasks_manager,
|
||||||
|
migration_manager,
|
||||||
|
main_controller,
|
||||||
|
import3,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.helper: Helpers = helper
|
self.helper: Helpers = helper
|
||||||
@ -77,11 +82,11 @@ class MainPrompt(cmd.Cmd):
|
|||||||
# get new password from user
|
# get new password from user
|
||||||
new_pass = getpass.getpass(prompt=f"NEW password for: {username} > ")
|
new_pass = getpass.getpass(prompt=f"NEW password for: {username} > ")
|
||||||
# check to make sure it fits our requirements.
|
# check to make sure it fits our requirements.
|
||||||
if len(new_pass) > 512:
|
if len(new_pass) < self.helper.minimum_password_length:
|
||||||
Console.warning("Passwords must be greater than 6char long and under 512")
|
Console.warning(
|
||||||
return False
|
"Passwords must be greater than"
|
||||||
if len(new_pass) < 6:
|
f" {self.helper.minimum_password_length} char long"
|
||||||
Console.warning("Passwords must be greater than 6char long and under 512")
|
)
|
||||||
return False
|
return False
|
||||||
# grab repeated password input
|
# grab repeated password input
|
||||||
new_pass_conf = getpass.getpass(prompt="Re-enter your password: > ")
|
new_pass_conf = getpass.getpass(prompt="Re-enter your password: > ")
|
||||||
|
@ -5,6 +5,10 @@ import pathlib
|
|||||||
import tempfile
|
import tempfile
|
||||||
import zipfile
|
import zipfile
|
||||||
from zipfile import ZipFile, ZIP_DEFLATED
|
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.helpers import Helpers
|
||||||
from app.classes.shared.console import Console
|
from app.classes.shared.console import Console
|
||||||
@ -19,6 +23,92 @@ class FileHelpers:
|
|||||||
def __init__(self, helper):
|
def __init__(self, helper):
|
||||||
self.helper: Helpers = 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
|
@staticmethod
|
||||||
def del_dirs(path):
|
def del_dirs(path):
|
||||||
path = pathlib.Path(path)
|
path = pathlib.Path(path)
|
||||||
|
@ -81,6 +81,7 @@ class Helpers:
|
|||||||
self.update_available = False
|
self.update_available = False
|
||||||
self.ignored_names = ["crafty_managed.txt", "db_stats"]
|
self.ignored_names = ["crafty_managed.txt", "db_stats"]
|
||||||
self.crafty_starting = False
|
self.crafty_starting = False
|
||||||
|
self.minimum_password_length = 8
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def auto_installer_fix(ex):
|
def auto_installer_fix(ex):
|
||||||
@ -117,7 +118,7 @@ class Helpers:
|
|||||||
Get latest bedrock executable url \n\n
|
Get latest bedrock executable url \n\n
|
||||||
returns url if successful, False if not
|
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 = {
|
headers = {
|
||||||
"Accept-Encoding": "identity",
|
"Accept-Encoding": "identity",
|
||||||
"Accept-Language": "en",
|
"Accept-Language": "en",
|
||||||
@ -1112,7 +1113,7 @@ class Helpers:
|
|||||||
return os.path.normpath(path)
|
return os.path.normpath(path)
|
||||||
|
|
||||||
def find_default_password(self):
|
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 = {}
|
data = {}
|
||||||
|
|
||||||
if Helpers.check_file_exists(default_file):
|
if Helpers.check_file_exists(default_file):
|
||||||
@ -1180,25 +1181,6 @@ class Helpers:
|
|||||||
return temp_dir
|
return temp_dir
|
||||||
return False
|
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
|
@staticmethod
|
||||||
def remove_prefix(text, prefix):
|
def remove_prefix(text, prefix):
|
||||||
if text.startswith(prefix):
|
if text.startswith(prefix):
|
||||||
|
@ -3,7 +3,6 @@ import time
|
|||||||
import shutil
|
import shutil
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
import urllib
|
|
||||||
|
|
||||||
from app.classes.controllers.server_perms_controller import PermissionsServers
|
from app.classes.controllers.server_perms_controller import PermissionsServers
|
||||||
from app.classes.controllers.servers_controller import ServersController
|
from app.classes.controllers.servers_controller import ServersController
|
||||||
@ -227,25 +226,39 @@ class ImportHelpers:
|
|||||||
download_thread.start()
|
download_thread.start()
|
||||||
|
|
||||||
def download_threaded_bedrock_server(self, path, new_id):
|
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:
|
try:
|
||||||
bedrock_url = Helpers.get_latest_bedrock_url()
|
bedrock_url = Helpers.get_latest_bedrock_url()
|
||||||
if bedrock_url.lower().startswith("https"):
|
if bedrock_url:
|
||||||
urllib.request.urlretrieve(
|
file_path = os.path.join(path, "bedrock_server.zip")
|
||||||
bedrock_url,
|
|
||||||
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(file_path)
|
||||||
unzip_path = self.helper.wtol_path(unzip_path)
|
# unzips archive that was downloaded.
|
||||||
# unzips archive that was downloaded.
|
FileHelpers.unzip_file(unzip_path)
|
||||||
FileHelpers.unzip_file(unzip_path)
|
# adjusts permissions for execution if os is not windows
|
||||||
# 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)
|
|
||||||
|
|
||||||
# we'll delete the zip we downloaded now
|
if not self.helper.is_os_windows():
|
||||||
os.remove(os.path.join(path, "bedrock_server.zip"))
|
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:
|
except Exception as e:
|
||||||
logger.critical(
|
logger.critical(
|
||||||
f"Failed to download bedrock executable during server creation! \n{e}"
|
f"Failed to download bedrock executable during server creation! \n{e}"
|
||||||
|
@ -239,7 +239,7 @@ class Controller:
|
|||||||
try:
|
try:
|
||||||
os.mkdir(final_path)
|
os.mkdir(final_path)
|
||||||
except FileExistsError:
|
except FileExistsError:
|
||||||
final_path += "_" + server["server_uuid"]
|
final_path += "_" + server["server_id"]
|
||||||
os.mkdir(final_path)
|
os.mkdir(final_path)
|
||||||
try:
|
try:
|
||||||
FileHelpers.copy_file(
|
FileHelpers.copy_file(
|
||||||
@ -632,11 +632,11 @@ class Controller:
|
|||||||
# and add the user to it if he's not a superuser
|
# and add the user to it if he's not a superuser
|
||||||
if len(captured_roles) == 0:
|
if len(captured_roles) == 0:
|
||||||
if not exec_user["superuser"]:
|
if not exec_user["superuser"]:
|
||||||
new_server_uuid = self.servers.get_server_data_by_id(new_server_id).get(
|
new_server_id = self.servers.get_server_data_by_id(new_server_id).get(
|
||||||
"server_uuid"
|
"server_id"
|
||||||
)
|
)
|
||||||
role_id = self.roles.add_role(
|
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"],
|
exec_user["user_id"],
|
||||||
)
|
)
|
||||||
self.server_perms.add_role_server(new_server_id, role_id, "11111111")
|
self.server_perms.add_role_server(new_server_id, role_id, "11111111")
|
||||||
@ -647,7 +647,7 @@ class Controller:
|
|||||||
role_id = role
|
role_id = role
|
||||||
self.server_perms.add_role_server(new_server_id, role_id, "11111111")
|
self.server_perms.add_role_server(new_server_id, role_id, "11111111")
|
||||||
|
|
||||||
return new_server_id, server_fs_uuid
|
return new_server_id
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def verify_jar_server(server_path: str, server_jar: str):
|
def verify_jar_server(server_path: str, server_jar: str):
|
||||||
@ -1095,7 +1095,7 @@ class Controller:
|
|||||||
for server in servers:
|
for server in servers:
|
||||||
server_path = server.get("path")
|
server_path = server.get("path")
|
||||||
new_local_server_path = os.path.join(
|
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):
|
if os.path.isdir(server_path):
|
||||||
WebSocketManager().broadcast_page(
|
WebSocketManager().broadcast_page(
|
||||||
|
@ -18,13 +18,22 @@ class DatabaseBuilder:
|
|||||||
logger.info("Fresh Install Detected - Creating Default Settings")
|
logger.info("Fresh Install Detected - Creating Default Settings")
|
||||||
Console.info("Fresh Install Detected - Creating Default Settings")
|
Console.info("Fresh Install Detected - Creating Default Settings")
|
||||||
default_data = self.helper.find_default_password()
|
default_data = self.helper.find_default_password()
|
||||||
if password not in default_data:
|
if "password" not in default_data:
|
||||||
Console.help(
|
Console.help(
|
||||||
"No default password found. Using password created "
|
"No default password found. Using password created "
|
||||||
"by Crafty. Find it in app/config/default-creds.txt"
|
"by Crafty. Find it in app/config/default-creds.txt"
|
||||||
)
|
)
|
||||||
username = default_data.get("username", "admin")
|
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(
|
self.users_helper.add_user(
|
||||||
username=username,
|
username=username,
|
||||||
|
@ -200,6 +200,21 @@ class Migrator(object):
|
|||||||
)
|
)
|
||||||
return model
|
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
|
@get_model
|
||||||
def rename_table(self, model: peewee.Model, new_name: str) -> peewee.Model:
|
def rename_table(self, model: peewee.Model, new_name: str) -> peewee.Model:
|
||||||
"""
|
"""
|
||||||
|
@ -10,7 +10,6 @@ import threading
|
|||||||
import logging.config
|
import logging.config
|
||||||
import subprocess
|
import subprocess
|
||||||
import html
|
import html
|
||||||
import urllib.request
|
|
||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
|
|
||||||
@ -209,7 +208,7 @@ class ServerInstance:
|
|||||||
self.dir_scheduler.start()
|
self.dir_scheduler.start()
|
||||||
self.start_dir_calc_task()
|
self.start_dir_calc_task()
|
||||||
self.backup_thread = threading.Thread(
|
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
|
self.is_backingup = False
|
||||||
# Reset crash and update at initialization
|
# Reset crash and update at initialization
|
||||||
@ -697,6 +696,10 @@ class ServerInstance:
|
|||||||
version_param = version[0][0].split(".")
|
version_param = version[0][0].split(".")
|
||||||
version_major = int(version_param[0])
|
version_major = int(version_param[0])
|
||||||
version_minor = int(version_param[1])
|
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
|
# Checking which version we are with
|
||||||
if version_major <= 1 and version_minor < 17:
|
if version_major <= 1 and version_minor < 17:
|
||||||
@ -730,8 +733,8 @@ class ServerInstance:
|
|||||||
server_obj.execution_command = execution_command
|
server_obj.execution_command = execution_command
|
||||||
Console.debug(SUCCESSMSG)
|
Console.debug(SUCCESSMSG)
|
||||||
|
|
||||||
elif version_major <= 1 and version_minor < 20:
|
elif version_major <= 1 and version_minor <= 20 and version_sub < 3:
|
||||||
# NEW VERSION >= 1.17 and <= 1.20
|
# NEW VERSION >= 1.17 and <= 1.20.2
|
||||||
# (no jar file in server dir, only run.bat and run.sh)
|
# (no jar file in server dir, only run.bat and run.sh)
|
||||||
|
|
||||||
run_file_path = ""
|
run_file_path = ""
|
||||||
@ -778,7 +781,7 @@ class ServerInstance:
|
|||||||
server_obj.execution_command = execution_command
|
server_obj.execution_command = execution_command
|
||||||
Console.debug(SUCCESSMSG)
|
Console.debug(SUCCESSMSG)
|
||||||
else:
|
else:
|
||||||
# NEW VERSION >= 1.20
|
# NEW VERSION >= 1.20.3
|
||||||
# (executable jar is back in server dir)
|
# (executable jar is back in server dir)
|
||||||
|
|
||||||
# Retrieving the executable jar filename
|
# Retrieving the executable jar filename
|
||||||
@ -1107,13 +1110,12 @@ class ServerInstance:
|
|||||||
f.write("eula=true")
|
f.write("eula=true")
|
||||||
self.run_threaded_server(user_id)
|
self.run_threaded_server(user_id)
|
||||||
|
|
||||||
@callback
|
def a_backup_server(self):
|
||||||
def backup_server(self):
|
|
||||||
if self.settings["backup_path"] == "":
|
if self.settings["backup_path"] == "":
|
||||||
logger.critical("Backup path is None. Canceling Backup!")
|
logger.critical("Backup path is None. Canceling Backup!")
|
||||||
return
|
return
|
||||||
backup_thread = threading.Thread(
|
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(
|
logger.info(
|
||||||
f"Starting Backup Thread for server {self.settings['server_name']}."
|
f"Starting Backup Thread for server {self.settings['server_name']}."
|
||||||
@ -1140,7 +1142,8 @@ class ServerInstance:
|
|||||||
return False
|
return False
|
||||||
logger.info(f"Backup Thread started for server {self.settings['server_name']}.")
|
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
|
was_server_running = None
|
||||||
logger.info(f"Starting server {self.name} (ID {self.server_id}) backup")
|
logger.info(f"Starting server {self.name} (ID {self.server_id}) backup")
|
||||||
server_users = PermissionsServers.get_server_user_list(self.server_id)
|
server_users = PermissionsServers.get_server_user_list(self.server_id)
|
||||||
@ -1370,7 +1373,7 @@ class ServerInstance:
|
|||||||
def a_jar_update(self):
|
def a_jar_update(self):
|
||||||
server_users = PermissionsServers.get_server_user_list(self.server_id)
|
server_users = PermissionsServers.get_server_user_list(self.server_id)
|
||||||
was_started = "-1"
|
was_started = "-1"
|
||||||
self.backup_server()
|
self.a_backup_server()
|
||||||
# checks if server is running. Calls shutdown if it is running.
|
# checks if server is running. Calls shutdown if it is running.
|
||||||
if self.check_running():
|
if self.check_running():
|
||||||
was_started = True
|
was_started = True
|
||||||
@ -1450,33 +1453,45 @@ class ServerInstance:
|
|||||||
|
|
||||||
# lets download the files
|
# lets download the files
|
||||||
if HelperServers.get_server_type_by_id(self.server_id) != "minecraft-bedrock":
|
if HelperServers.get_server_type_by_id(self.server_id) != "minecraft-bedrock":
|
||||||
# boolean returns true for false for success
|
|
||||||
downloaded = Helpers.download_file(
|
jar_dir = os.path.dirname(current_executable)
|
||||||
self.settings["executable_update_url"], 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:
|
else:
|
||||||
# downloads zip from remote url
|
# downloads zip from remote url
|
||||||
try:
|
try:
|
||||||
bedrock_url = Helpers.get_latest_bedrock_url()
|
bedrock_url = Helpers.get_latest_bedrock_url()
|
||||||
if bedrock_url.lower().startswith("https"):
|
if bedrock_url:
|
||||||
urllib.request.urlretrieve(
|
# Use the new method for secure download
|
||||||
bedrock_url,
|
download_path = os.path.join(
|
||||||
os.path.join(self.settings["path"], "bedrock_server.zip"),
|
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")
|
if downloaded:
|
||||||
unzip_path = self.helper.wtol_path(unzip_path)
|
unzip_path = download_path
|
||||||
# unzips archive that was downloaded.
|
unzip_path = self.helper.wtol_path(unzip_path)
|
||||||
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
|
# unzips archive that was downloaded.
|
||||||
os.remove(os.path.join(self.settings["path"], "bedrock_server.zip"))
|
FileHelpers.unzip_file(unzip_path, server_update=True)
|
||||||
downloaded = 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:
|
except Exception as e:
|
||||||
logger.critical(
|
logger.critical(
|
||||||
f"Failed to download bedrock executable for update \n{e}"
|
f"Failed to download bedrock executable for update \n{e}"
|
||||||
|
@ -140,7 +140,7 @@ class TasksManager:
|
|||||||
)
|
)
|
||||||
|
|
||||||
elif command == "backup_server":
|
elif command == "backup_server":
|
||||||
svr.backup_server()
|
svr.a_backup_server()
|
||||||
|
|
||||||
elif command == "update_executable":
|
elif command == "update_executable":
|
||||||
svr.jar_update()
|
svr.jar_update()
|
||||||
|
@ -174,7 +174,7 @@ class PanelHandler(BaseHandler):
|
|||||||
self.redirect("/panel/error?error=Invalid Server ID")
|
self.redirect("/panel/error?error=Invalid Server ID")
|
||||||
return None
|
return None
|
||||||
for server in self.controller.servers.failed_servers:
|
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
|
self.failed_server = True
|
||||||
return server_id
|
return server_id
|
||||||
# Does this server exist?
|
# Does this server exist?
|
||||||
@ -345,15 +345,17 @@ class PanelHandler(BaseHandler):
|
|||||||
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
|
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
|
||||||
),
|
),
|
||||||
"super_user": superuser,
|
"super_user": superuser,
|
||||||
"api_key": {
|
"api_key": (
|
||||||
"name": api_key.name,
|
{
|
||||||
"created": api_key.created,
|
"name": api_key.name,
|
||||||
"server_permissions": api_key.server_permissions,
|
"created": api_key.created,
|
||||||
"crafty_permissions": api_key.crafty_permissions,
|
"server_permissions": api_key.server_permissions,
|
||||||
"superuser": api_key.superuser,
|
"crafty_permissions": api_key.crafty_permissions,
|
||||||
}
|
"superuser": api_key.superuser,
|
||||||
if api_key is not None
|
}
|
||||||
else None,
|
if api_key is not None
|
||||||
|
else None
|
||||||
|
),
|
||||||
"superuser": superuser,
|
"superuser": superuser,
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
@ -417,14 +419,14 @@ class PanelHandler(BaseHandler):
|
|||||||
self.controller.first_login = False
|
self.controller.first_login = False
|
||||||
if superuser: # TODO: Figure out a better solution
|
if superuser: # TODO: Figure out a better solution
|
||||||
try:
|
try:
|
||||||
page_data[
|
page_data["servers"] = (
|
||||||
"servers"
|
self.controller.servers.get_all_servers_stats()
|
||||||
] = self.controller.servers.get_all_servers_stats()
|
)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
self.controller.servers.stats.record_stats()
|
self.controller.servers.stats.record_stats()
|
||||||
page_data[
|
page_data["servers"] = (
|
||||||
"servers"
|
self.controller.servers.get_all_servers_stats()
|
||||||
] = self.controller.servers.get_all_servers_stats()
|
)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
user_auth = self.controller.servers.get_authorized_servers_stats(
|
user_auth = self.controller.servers.get_authorized_servers_stats(
|
||||||
@ -454,19 +456,19 @@ class PanelHandler(BaseHandler):
|
|||||||
for server_id in user_order[:]:
|
for server_id in user_order[:]:
|
||||||
for server in un_used_servers[:]:
|
for server in un_used_servers[:]:
|
||||||
if flag == 0:
|
if flag == 0:
|
||||||
server["stats"][
|
server["stats"]["importing"] = (
|
||||||
"importing"
|
self.controller.servers.get_import_status(
|
||||||
] = self.controller.servers.get_import_status(
|
str(server["stats"]["server_id"]["server_id"])
|
||||||
str(server["stats"]["server_id"]["server_id"])
|
)
|
||||||
)
|
)
|
||||||
server["stats"]["crashed"] = self.controller.servers.is_crashed(
|
server["stats"]["crashed"] = self.controller.servers.is_crashed(
|
||||||
str(server["stats"]["server_id"]["server_id"])
|
str(server["stats"]["server_id"]["server_id"])
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
server["stats"][
|
server["stats"]["waiting_start"] = (
|
||||||
"waiting_start"
|
self.controller.servers.get_waiting_start(
|
||||||
] = self.controller.servers.get_waiting_start(
|
str(server["stats"]["server_id"]["server_id"])
|
||||||
str(server["stats"]["server_id"]["server_id"])
|
)
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to get server waiting to start: {e}")
|
logger.error(f"Failed to get server waiting to start: {e}")
|
||||||
@ -543,9 +545,9 @@ class PanelHandler(BaseHandler):
|
|||||||
server_id
|
server_id
|
||||||
)
|
)
|
||||||
if not self.failed_server:
|
if not self.failed_server:
|
||||||
page_data[
|
page_data["server_stats"] = (
|
||||||
"server_stats"
|
self.controller.servers.get_server_stats_by_id(server_id)
|
||||||
] = self.controller.servers.get_server_stats_by_id(server_id)
|
)
|
||||||
else:
|
else:
|
||||||
server_temp_obj = self.controller.servers.get_server_data_by_id(
|
server_temp_obj = self.controller.servers.get_server_data_by_id(
|
||||||
server_id
|
server_id
|
||||||
@ -554,7 +556,7 @@ class PanelHandler(BaseHandler):
|
|||||||
"server_id": {
|
"server_id": {
|
||||||
"server_id": server_id,
|
"server_id": server_id,
|
||||||
"server_name": server_temp_obj["server_name"],
|
"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"],
|
"path": server_temp_obj["path"],
|
||||||
"log_path": server_temp_obj["log_path"],
|
"log_path": server_temp_obj["log_path"],
|
||||||
"executable": server_temp_obj["executable"],
|
"executable": server_temp_obj["executable"],
|
||||||
@ -572,6 +574,7 @@ class PanelHandler(BaseHandler):
|
|||||||
"crash_detection": server_temp_obj["crash_detection"],
|
"crash_detection": server_temp_obj["crash_detection"],
|
||||||
"show_status": server_temp_obj["show_status"],
|
"show_status": server_temp_obj["show_status"],
|
||||||
"ignored_exits": server_temp_obj["ignored_exits"],
|
"ignored_exits": server_temp_obj["ignored_exits"],
|
||||||
|
"count_players": server_temp_obj["count_players"],
|
||||||
},
|
},
|
||||||
"running": False,
|
"running": False,
|
||||||
"crashed": False,
|
"crashed": False,
|
||||||
@ -611,19 +614,19 @@ class PanelHandler(BaseHandler):
|
|||||||
"Config": EnumPermissionsServer.CONFIG,
|
"Config": EnumPermissionsServer.CONFIG,
|
||||||
"Players": EnumPermissionsServer.PLAYERS,
|
"Players": EnumPermissionsServer.PLAYERS,
|
||||||
}
|
}
|
||||||
page_data[
|
page_data["user_permissions"] = (
|
||||||
"user_permissions"
|
self.controller.server_perms.get_user_id_permissions_list(
|
||||||
] = self.controller.server_perms.get_user_id_permissions_list(
|
exec_user["user_id"], server_id
|
||||||
exec_user["user_id"], server_id
|
)
|
||||||
)
|
)
|
||||||
if not self.failed_server:
|
if not self.failed_server:
|
||||||
page_data["server_stats"][
|
page_data["server_stats"]["crashed"] = (
|
||||||
"crashed"
|
self.controller.servers.is_crashed(server_id)
|
||||||
] = self.controller.servers.is_crashed(server_id)
|
)
|
||||||
if not self.failed_server:
|
if not self.failed_server:
|
||||||
page_data["server_stats"][
|
page_data["server_stats"]["server_type"] = (
|
||||||
"server_type"
|
self.controller.servers.get_server_type_by_id(server_id)
|
||||||
] = self.controller.servers.get_server_type_by_id(server_id)
|
)
|
||||||
|
|
||||||
if not subpage:
|
if not subpage:
|
||||||
for spage, perm in SUBPAGE_PERMS.items():
|
for spage, perm in SUBPAGE_PERMS.items():
|
||||||
@ -674,23 +677,23 @@ class PanelHandler(BaseHandler):
|
|||||||
page_data["java_versions"] = page_java
|
page_data["java_versions"] = page_java
|
||||||
if subpage == "backup":
|
if subpage == "backup":
|
||||||
server_info = self.controller.servers.get_server_data_by_id(server_id)
|
server_info = self.controller.servers.get_server_data_by_id(server_id)
|
||||||
page_data[
|
page_data["backup_config"] = (
|
||||||
"backup_config"
|
self.controller.management.get_backup_config(server_id)
|
||||||
] = self.controller.management.get_backup_config(server_id)
|
)
|
||||||
exclusions = []
|
exclusions = []
|
||||||
page_data[
|
page_data["exclusions"] = (
|
||||||
"exclusions"
|
self.controller.management.get_excluded_backup_dirs(server_id)
|
||||||
] = self.controller.management.get_excluded_backup_dirs(server_id)
|
)
|
||||||
page_data[
|
page_data["backing_up"] = (
|
||||||
"backing_up"
|
self.controller.servers.get_server_instance_by_id(
|
||||||
] = self.controller.servers.get_server_instance_by_id(
|
server_id
|
||||||
server_id
|
).is_backingup
|
||||||
).is_backingup
|
)
|
||||||
page_data[
|
page_data["backup_stats"] = (
|
||||||
"backup_stats"
|
self.controller.servers.get_server_instance_by_id(
|
||||||
] = self.controller.servers.get_server_instance_by_id(
|
server_id
|
||||||
server_id
|
).send_backup_status()
|
||||||
).send_backup_status()
|
)
|
||||||
# makes it so relative path is the only thing shown
|
# makes it so relative path is the only thing shown
|
||||||
for file in page_data["exclusions"]:
|
for file in page_data["exclusions"]:
|
||||||
if Helpers.is_os_windows():
|
if Helpers.is_os_windows():
|
||||||
@ -723,10 +726,10 @@ class PanelHandler(BaseHandler):
|
|||||||
server_id, hours=(days * 24)
|
server_id, hours=(days * 24)
|
||||||
)
|
)
|
||||||
if subpage == "webhooks":
|
if subpage == "webhooks":
|
||||||
page_data[
|
page_data["webhooks"] = (
|
||||||
"webhooks"
|
self.controller.management.get_webhooks_by_server(
|
||||||
] = self.controller.management.get_webhooks_by_server(
|
server_id, model=True
|
||||||
server_id, model=True
|
)
|
||||||
)
|
)
|
||||||
page_data["triggers"] = WebhookFactory.get_monitored_events()
|
page_data["triggers"] = WebhookFactory.get_monitored_events()
|
||||||
|
|
||||||
@ -758,9 +761,9 @@ class PanelHandler(BaseHandler):
|
|||||||
if not superuser:
|
if not superuser:
|
||||||
self.redirect("/panel/error?error=Unauthorized access")
|
self.redirect("/panel/error?error=Unauthorized access")
|
||||||
page_data["banned_players_html"] = get_banned_players_html()
|
page_data["banned_players_html"] = get_banned_players_html()
|
||||||
page_data[
|
page_data["banned_players"] = (
|
||||||
"banned_players"
|
self.controller.servers.get_banned_players(server_id)
|
||||||
] = self.controller.servers.get_banned_players(server_id)
|
)
|
||||||
server_instance = self.controller.servers.get_server_instance_by_id(
|
server_instance = self.controller.servers.get_server_instance_by_id(
|
||||||
server_id
|
server_id
|
||||||
)
|
)
|
||||||
@ -925,9 +928,9 @@ class PanelHandler(BaseHandler):
|
|||||||
if item not in page_data["backgrounds"]:
|
if item not in page_data["backgrounds"]:
|
||||||
page_data["backgrounds"].append(item)
|
page_data["backgrounds"].append(item)
|
||||||
page_data["background"] = self.controller.cached_login
|
page_data["background"] = self.controller.cached_login
|
||||||
page_data[
|
page_data["login_opacity"] = (
|
||||||
"login_opacity"
|
self.controller.management.get_login_opacity()
|
||||||
] = self.controller.management.get_login_opacity()
|
)
|
||||||
|
|
||||||
page_data["active_link"] = "custom_login"
|
page_data["active_link"] = "custom_login"
|
||||||
template = "panel/custom_login.html"
|
template = "panel/custom_login.html"
|
||||||
@ -959,13 +962,11 @@ class PanelHandler(BaseHandler):
|
|||||||
page_data["servers"] = []
|
page_data["servers"] = []
|
||||||
page_data["servers_all"] = self.controller.servers.get_all_defined_servers()
|
page_data["servers_all"] = self.controller.servers.get_all_defined_servers()
|
||||||
page_data["role-servers"] = []
|
page_data["role-servers"] = []
|
||||||
page_data[
|
page_data["permissions_all"] = (
|
||||||
"permissions_all"
|
self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||||
] = self.controller.crafty_perms.list_defined_crafty_permissions()
|
)
|
||||||
page_data["permissions_list"] = set()
|
page_data["permissions_list"] = set()
|
||||||
page_data[
|
page_data["quantity_server"] = (
|
||||||
"quantity_server"
|
|
||||||
] = (
|
|
||||||
self.controller.crafty_perms.list_all_crafty_permissions_quantity_limits() # pylint: disable=line-too-long
|
self.controller.crafty_perms.list_all_crafty_permissions_quantity_limits() # pylint: disable=line-too-long
|
||||||
)
|
)
|
||||||
page_data["languages"] = []
|
page_data["languages"] = []
|
||||||
@ -1007,10 +1008,10 @@ class PanelHandler(BaseHandler):
|
|||||||
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
|
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
|
||||||
server_id
|
server_id
|
||||||
)
|
)
|
||||||
page_data[
|
page_data["user_permissions"] = (
|
||||||
"user_permissions"
|
self.controller.server_perms.get_user_id_permissions_list(
|
||||||
] = self.controller.server_perms.get_user_id_permissions_list(
|
exec_user["user_id"], server_id
|
||||||
exec_user["user_id"], server_id
|
)
|
||||||
)
|
)
|
||||||
page_data["permissions"] = {
|
page_data["permissions"] = {
|
||||||
"Commands": EnumPermissionsServer.COMMANDS,
|
"Commands": EnumPermissionsServer.COMMANDS,
|
||||||
@ -1025,9 +1026,9 @@ class PanelHandler(BaseHandler):
|
|||||||
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
|
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
|
||||||
server_id
|
server_id
|
||||||
)
|
)
|
||||||
page_data["server_stats"][
|
page_data["server_stats"]["server_type"] = (
|
||||||
"server_type"
|
self.controller.servers.get_server_type_by_id(server_id)
|
||||||
] = self.controller.servers.get_server_type_by_id(server_id)
|
)
|
||||||
page_data["new_webhook"] = True
|
page_data["new_webhook"] = True
|
||||||
page_data["webhook"] = {}
|
page_data["webhook"] = {}
|
||||||
page_data["webhook"]["webhook_type"] = "Custom"
|
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(
|
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
|
||||||
server_id
|
server_id
|
||||||
)
|
)
|
||||||
page_data[
|
page_data["user_permissions"] = (
|
||||||
"user_permissions"
|
self.controller.server_perms.get_user_id_permissions_list(
|
||||||
] = self.controller.server_perms.get_user_id_permissions_list(
|
exec_user["user_id"], server_id
|
||||||
exec_user["user_id"], server_id
|
)
|
||||||
)
|
)
|
||||||
page_data["permissions"] = {
|
page_data["permissions"] = {
|
||||||
"Commands": EnumPermissionsServer.COMMANDS,
|
"Commands": EnumPermissionsServer.COMMANDS,
|
||||||
@ -1079,9 +1080,9 @@ class PanelHandler(BaseHandler):
|
|||||||
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
|
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
|
||||||
server_id
|
server_id
|
||||||
)
|
)
|
||||||
page_data["server_stats"][
|
page_data["server_stats"]["server_type"] = (
|
||||||
"server_type"
|
self.controller.servers.get_server_type_by_id(server_id)
|
||||||
] = self.controller.servers.get_server_type_by_id(server_id)
|
)
|
||||||
page_data["new_webhook"] = False
|
page_data["new_webhook"] = False
|
||||||
page_data["webhook"] = self.controller.management.get_webhook_by_id(
|
page_data["webhook"] = self.controller.management.get_webhook_by_id(
|
||||||
webhook_id
|
webhook_id
|
||||||
@ -1121,10 +1122,10 @@ class PanelHandler(BaseHandler):
|
|||||||
"Config": EnumPermissionsServer.CONFIG,
|
"Config": EnumPermissionsServer.CONFIG,
|
||||||
"Players": EnumPermissionsServer.PLAYERS,
|
"Players": EnumPermissionsServer.PLAYERS,
|
||||||
}
|
}
|
||||||
page_data[
|
page_data["user_permissions"] = (
|
||||||
"user_permissions"
|
self.controller.server_perms.get_user_id_permissions_list(
|
||||||
] = self.controller.server_perms.get_user_id_permissions_list(
|
exec_user["user_id"], server_id
|
||||||
exec_user["user_id"], server_id
|
)
|
||||||
)
|
)
|
||||||
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
|
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
|
||||||
server_id
|
server_id
|
||||||
@ -1132,9 +1133,9 @@ class PanelHandler(BaseHandler):
|
|||||||
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
|
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
|
||||||
server_id
|
server_id
|
||||||
)
|
)
|
||||||
page_data["server_stats"][
|
page_data["server_stats"]["server_type"] = (
|
||||||
"server_type"
|
self.controller.servers.get_server_type_by_id(server_id)
|
||||||
] = self.controller.servers.get_server_type_by_id(server_id)
|
)
|
||||||
page_data["new_schedule"] = True
|
page_data["new_schedule"] = True
|
||||||
page_data["schedule"] = {}
|
page_data["schedule"] = {}
|
||||||
page_data["schedule"]["children"] = []
|
page_data["schedule"]["children"] = []
|
||||||
@ -1189,10 +1190,10 @@ class PanelHandler(BaseHandler):
|
|||||||
"Config": EnumPermissionsServer.CONFIG,
|
"Config": EnumPermissionsServer.CONFIG,
|
||||||
"Players": EnumPermissionsServer.PLAYERS,
|
"Players": EnumPermissionsServer.PLAYERS,
|
||||||
}
|
}
|
||||||
page_data[
|
page_data["user_permissions"] = (
|
||||||
"user_permissions"
|
self.controller.server_perms.get_user_id_permissions_list(
|
||||||
] = self.controller.server_perms.get_user_id_permissions_list(
|
exec_user["user_id"], server_id
|
||||||
exec_user["user_id"], server_id
|
)
|
||||||
)
|
)
|
||||||
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
|
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
|
||||||
server_id
|
server_id
|
||||||
@ -1200,9 +1201,9 @@ class PanelHandler(BaseHandler):
|
|||||||
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
|
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
|
||||||
server_id
|
server_id
|
||||||
)
|
)
|
||||||
page_data["server_stats"][
|
page_data["server_stats"]["server_type"] = (
|
||||||
"server_type"
|
self.controller.servers.get_server_type_by_id(server_id)
|
||||||
] = self.controller.servers.get_server_type_by_id(server_id)
|
)
|
||||||
page_data["new_schedule"] = False
|
page_data["new_schedule"] = False
|
||||||
page_data["schedule"] = {}
|
page_data["schedule"] = {}
|
||||||
page_data["schedule"]["server_id"] = server_id
|
page_data["schedule"]["server_id"] = server_id
|
||||||
@ -1212,9 +1213,9 @@ class PanelHandler(BaseHandler):
|
|||||||
page_data["schedule"]["name"] = schedule.name
|
page_data["schedule"]["name"] = schedule.name
|
||||||
else:
|
else:
|
||||||
page_data["schedule"]["name"] = ""
|
page_data["schedule"]["name"] = ""
|
||||||
page_data["schedule"][
|
page_data["schedule"]["children"] = (
|
||||||
"children"
|
self.controller.management.get_child_schedules(sch_id)
|
||||||
] = self.controller.management.get_child_schedules(sch_id)
|
)
|
||||||
# We check here to see if the command is any of the default ones.
|
# 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
|
# We do not want a user changing to a custom command
|
||||||
# and seeing our command there.
|
# and seeing our command there.
|
||||||
@ -1280,16 +1281,16 @@ class PanelHandler(BaseHandler):
|
|||||||
}
|
}
|
||||||
if exec_user["superuser"]:
|
if exec_user["superuser"]:
|
||||||
page_data["users"] = self.controller.users.get_all_users()
|
page_data["users"] = self.controller.users.get_all_users()
|
||||||
page_data[
|
page_data["permissions_all"] = (
|
||||||
"permissions_all"
|
self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||||
] = self.controller.crafty_perms.list_defined_crafty_permissions()
|
)
|
||||||
page_data[
|
page_data["permissions_list"] = (
|
||||||
"permissions_list"
|
self.controller.crafty_perms.get_crafty_permissions_list(user_id)
|
||||||
] = self.controller.crafty_perms.get_crafty_permissions_list(user_id)
|
)
|
||||||
page_data[
|
page_data["quantity_server"] = (
|
||||||
"quantity_server"
|
self.controller.crafty_perms.list_crafty_permissions_quantity_limits(
|
||||||
] = self.controller.crafty_perms.list_crafty_permissions_quantity_limits(
|
user_id
|
||||||
user_id
|
)
|
||||||
)
|
)
|
||||||
page_data["languages"] = []
|
page_data["languages"] = []
|
||||||
page_data["languages"].append(
|
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["user"] = self.controller.users.get_user_by_id(user_id)
|
||||||
page_data["api_keys"] = self.controller.users.get_user_api_keys(user_id)
|
page_data["api_keys"] = self.controller.users.get_user_api_keys(user_id)
|
||||||
# self.controller.crafty_perms.list_defined_crafty_permissions()
|
# self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||||
page_data[
|
page_data["server_permissions_all"] = (
|
||||||
"server_permissions_all"
|
self.controller.server_perms.list_defined_permissions()
|
||||||
] = self.controller.server_perms.list_defined_permissions()
|
)
|
||||||
page_data[
|
page_data["crafty_permissions_all"] = (
|
||||||
"crafty_permissions_all"
|
self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||||
] = self.controller.crafty_perms.list_defined_crafty_permissions()
|
)
|
||||||
|
|
||||||
if user_id is None:
|
if user_id is None:
|
||||||
self.redirect("/panel/error?error=Invalid User ID")
|
self.redirect("/panel/error?error=Invalid User ID")
|
||||||
@ -1442,9 +1443,9 @@ class PanelHandler(BaseHandler):
|
|||||||
DatabaseShortcuts.get_data_obj(server.server_object)
|
DatabaseShortcuts.get_data_obj(server.server_object)
|
||||||
)
|
)
|
||||||
page_data["servers_all"] = page_servers
|
page_data["servers_all"] = page_servers
|
||||||
page_data[
|
page_data["permissions_all"] = (
|
||||||
"permissions_all"
|
self.controller.server_perms.list_defined_permissions()
|
||||||
] = self.controller.server_perms.list_defined_permissions()
|
)
|
||||||
page_data["permissions_dict"] = {}
|
page_data["permissions_dict"] = {}
|
||||||
template = "panel/panel_edit_role.html"
|
template = "panel/panel_edit_role.html"
|
||||||
|
|
||||||
@ -1467,12 +1468,12 @@ class PanelHandler(BaseHandler):
|
|||||||
DatabaseShortcuts.get_data_obj(server.server_object)
|
DatabaseShortcuts.get_data_obj(server.server_object)
|
||||||
)
|
)
|
||||||
page_data["servers_all"] = page_servers
|
page_data["servers_all"] = page_servers
|
||||||
page_data[
|
page_data["permissions_all"] = (
|
||||||
"permissions_all"
|
self.controller.server_perms.list_defined_permissions()
|
||||||
] = self.controller.server_perms.list_defined_permissions()
|
)
|
||||||
page_data[
|
page_data["permissions_dict"] = (
|
||||||
"permissions_dict"
|
self.controller.server_perms.get_role_permissions_dict(role_id)
|
||||||
] = self.controller.server_perms.get_role_permissions_dict(role_id)
|
)
|
||||||
page_data["user-roles"] = user_roles
|
page_data["user-roles"] = user_roles
|
||||||
page_data["users"] = self.controller.users.get_all_users()
|
page_data["users"] = self.controller.users.get_all_users()
|
||||||
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import json
|
||||||
import nh3
|
import nh3
|
||||||
|
from jsonschema import validate
|
||||||
|
from jsonschema.exceptions import ValidationError
|
||||||
|
|
||||||
from app.classes.shared.helpers import Helpers
|
from app.classes.shared.helpers import Helpers
|
||||||
from app.classes.models.users import HelperUsers
|
from app.classes.models.users import HelperUsers
|
||||||
@ -45,7 +48,7 @@ class PublicHandler(BaseHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.request.query:
|
if self.request.query:
|
||||||
page_data["query"] = self.request.query
|
page_data["query"] = self.request.query_arguments.get("next")[0].decode()
|
||||||
|
|
||||||
# sensible defaults
|
# sensible defaults
|
||||||
template = "public/404.html"
|
template = "public/404.html"
|
||||||
@ -75,11 +78,7 @@ class PublicHandler(BaseHandler):
|
|||||||
|
|
||||||
# if we have no page, let's go to login
|
# if we have no page, let's go to login
|
||||||
else:
|
else:
|
||||||
if self.request.query:
|
return self.redirect("/login")
|
||||||
self.redirect("/login?" + self.request.query)
|
|
||||||
else:
|
|
||||||
self.redirect("/login")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.render(
|
self.render(
|
||||||
template,
|
template,
|
||||||
@ -89,33 +88,61 @@ class PublicHandler(BaseHandler):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def post(self, page=None):
|
def post(self, page=None):
|
||||||
# pylint: disable=no-member
|
login_schema = {
|
||||||
error = nh3.clean(self.get_argument("error", "Invalid Login!"))
|
"type": "object",
|
||||||
error_msg = nh3.clean(self.get_argument("error_msg", ""))
|
"properties": {
|
||||||
# pylint: enable=no-member
|
"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 = {
|
page_data = {
|
||||||
"version": self.helper.get_version_string(),
|
"version": self.helper.get_version_string(),
|
||||||
"error": error,
|
|
||||||
"lang": self.helper.get_setting("language"),
|
"lang": self.helper.get_setting("language"),
|
||||||
"lang_page": self.helper.get_lang_page(self.helper.get_setting("language")),
|
"lang_page": self.helper.get_lang_page(self.helper.get_setting("language")),
|
||||||
"query": "",
|
"query": "",
|
||||||
}
|
}
|
||||||
if self.request.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":
|
if page == "login":
|
||||||
|
data = json.loads(self.request.body)
|
||||||
|
|
||||||
auth_log.info(
|
auth_log.info(
|
||||||
f"User attempting to authenticate from {self.get_remote_ip()}"
|
f"User attempting to authenticate from {self.get_remote_ip()}"
|
||||||
)
|
)
|
||||||
next_page = "/login"
|
entered_username = nh3.clean(data["username"]) # pylint: disable=no-member
|
||||||
if self.request.query:
|
entered_password = data["password"]
|
||||||
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
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user_id = HelperUsers.get_user_id_by_name(entered_username.lower())
|
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()}"
|
f" Authentication failed from remote IP {self.get_remote_ip()}"
|
||||||
" Users does not exist."
|
" 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")
|
||||||
# self.clear_cookie("user_data")
|
# self.clear_cookie("user_data")
|
||||||
self.clear_cookie("token")
|
return 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
|
|
||||||
|
|
||||||
# if we don't have a user
|
# if we don't have a user
|
||||||
if not user_data:
|
if not user_data:
|
||||||
auth_log.error(
|
auth_log.error(
|
||||||
@ -145,15 +174,18 @@ class PublicHandler(BaseHandler):
|
|||||||
" User does not exist."
|
" User does not exist."
|
||||||
)
|
)
|
||||||
self.controller.log_attempt(self.get_remote_ip(), entered_username)
|
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")
|
||||||
# self.clear_cookie("user_data")
|
# self.clear_cookie("user_data")
|
||||||
self.clear_cookie("token")
|
return 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
|
|
||||||
|
|
||||||
# if they are disabled
|
# if they are disabled
|
||||||
if not user_data.enabled:
|
if not user_data.enabled:
|
||||||
@ -163,19 +195,18 @@ class PublicHandler(BaseHandler):
|
|||||||
" User account disabled"
|
" User account disabled"
|
||||||
)
|
)
|
||||||
self.controller.log_attempt(self.get_remote_ip(), entered_username)
|
self.controller.log_attempt(self.get_remote_ip(), entered_username)
|
||||||
error_msg = (
|
self.finish_json(
|
||||||
"User account disabled. Please contact "
|
403,
|
||||||
"your system administrator for more info."
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": self.helper.translation.translate(
|
||||||
|
"login", "disabled", self.helper.get_setting("language")
|
||||||
|
),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
# self.clear_cookie("user")
|
# self.clear_cookie("user")
|
||||||
# self.clear_cookie("user_data")
|
# self.clear_cookie("user_data")
|
||||||
self.clear_cookie("token")
|
return 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
|
|
||||||
|
|
||||||
login_result = self.helper.verify_pass(entered_password, user_data.password)
|
login_result = self.helper.verify_pass(entered_password, user_data.password)
|
||||||
|
|
||||||
# Valid Login
|
# Valid Login
|
||||||
@ -200,32 +231,34 @@ class PublicHandler(BaseHandler):
|
|||||||
user_data.user_id, "Logged in", 0, self.get_remote_ip()
|
user_data.user_id, "Logged in", 0, self.get_remote_ip()
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.request.query_arguments.get("next"):
|
return self.finish_json(
|
||||||
next_page = self.request.query_arguments.get("next")[0].decode()
|
200, {"status": "ok", "data": {"message": "login successful!"}}
|
||||||
else:
|
)
|
||||||
next_page = "/panel/dashboard"
|
|
||||||
|
|
||||||
self.redirect(next_page)
|
# We'll continue on and handle unsuccessful logins
|
||||||
else:
|
auth_log.error(
|
||||||
auth_log.error(
|
f"User attempted to log into {entered_username}."
|
||||||
f"User attempted to log into {entered_username}."
|
f" Authentication failed from remote IP {self.get_remote_ip()}"
|
||||||
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)
|
# log this failed login attempt
|
||||||
# self.clear_cookie("user")
|
self.controller.management.add_to_audit_log(
|
||||||
# self.clear_cookie("user_data")
|
user_data.user_id, "Tried to log in", 0, self.get_remote_ip()
|
||||||
self.clear_cookie("token")
|
)
|
||||||
error_msg = "Incorrect username or password. Please try again."
|
return self.finish_json(
|
||||||
# log this failed login attempt
|
403,
|
||||||
self.controller.management.add_to_audit_log(
|
{"status": "error", "error": error_msg},
|
||||||
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}")
|
|
||||||
else:
|
else:
|
||||||
if self.request.query:
|
self.redirect("/login?")
|
||||||
self.redirect("/login?" + self.request.query)
|
|
||||||
else:
|
|
||||||
self.redirect("/login")
|
|
||||||
|
@ -208,92 +208,92 @@ def api_handlers(handler_args):
|
|||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/?",
|
r"/api/v2/servers/([a-z0-9-]+)/?",
|
||||||
ApiServersServerIndexHandler,
|
ApiServersServerIndexHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/backups/?",
|
r"/api/v2/servers/([a-z0-9-]+)/backups/?",
|
||||||
ApiServersServerBackupsIndexHandler,
|
ApiServersServerBackupsIndexHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/backups/backup/?",
|
r"/api/v2/servers/([a-z0-9-]+)/backups/backup/?",
|
||||||
ApiServersServerBackupsBackupIndexHandler,
|
ApiServersServerBackupsBackupIndexHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/files/?",
|
r"/api/v2/servers/([a-z0-9-]+)/files/?",
|
||||||
ApiServersServerFilesIndexHandler,
|
ApiServersServerFilesIndexHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/files/create/?",
|
r"/api/v2/servers/([a-z0-9-]+)/files/create/?",
|
||||||
ApiServersServerFilesCreateHandler,
|
ApiServersServerFilesCreateHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/files/zip/?",
|
r"/api/v2/servers/([a-z0-9-]+)/files/zip/?",
|
||||||
ApiServersServerFilesZipHandler,
|
ApiServersServerFilesZipHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/tasks/?",
|
r"/api/v2/servers/([a-z0-9-]+)/tasks/?",
|
||||||
ApiServersServerTasksIndexHandler,
|
ApiServersServerTasksIndexHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/tasks/([0-9]+)/?",
|
r"/api/v2/servers/([a-z0-9-]+)/tasks/([0-9]+)/?",
|
||||||
ApiServersServerTasksTaskIndexHandler,
|
ApiServersServerTasksTaskIndexHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/tasks/([0-9]+)/children/?",
|
r"/api/v2/servers/([a-z0-9-]+)/tasks/([0-9]+)/children/?",
|
||||||
ApiServersServerTasksTaskChildrenHandler,
|
ApiServersServerTasksTaskChildrenHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/stats/?",
|
r"/api/v2/servers/([a-z0-9-]+)/stats/?",
|
||||||
ApiServersServerStatsHandler,
|
ApiServersServerStatsHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/history/?",
|
r"/api/v2/servers/([a-z0-9-]+)/history/?",
|
||||||
ApiServersServerHistoryHandler,
|
ApiServersServerHistoryHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/webhook/([0-9]+)/?",
|
r"/api/v2/servers/([a-z0-9-]+)/webhook/([0-9]+)/?",
|
||||||
ApiServersServerWebhooksManagementIndexHandler,
|
ApiServersServerWebhooksManagementIndexHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/webhook/?",
|
r"/api/v2/servers/([a-z0-9-]+)/webhook/?",
|
||||||
ApiServersServerWebhooksIndexHandler,
|
ApiServersServerWebhooksIndexHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/action/([a-z_]+)/?",
|
r"/api/v2/servers/([a-z0-9-]+)/action/([a-z_]+)/?",
|
||||||
ApiServersServerActionHandler,
|
ApiServersServerActionHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/logs/?",
|
r"/api/v2/servers/([a-z0-9-]+)/logs/?",
|
||||||
ApiServersServerLogsHandler,
|
ApiServersServerLogsHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/users/?",
|
r"/api/v2/servers/([a-z0-9-]+)/users/?",
|
||||||
ApiServersServerUsersHandler,
|
ApiServersServerUsersHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/public/?",
|
r"/api/v2/servers/([a-z0-9-]+)/public/?",
|
||||||
ApiServersServerPublicHandler,
|
ApiServersServerPublicHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([0-9]+)/stdin/?",
|
r"/api/v2/servers/([a-z0-9-]+)/stdin/?",
|
||||||
ApiServersServerStdinHandler,
|
ApiServersServerStdinHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
|
@ -17,7 +17,7 @@ login_schema = {
|
|||||||
"minLength": 4,
|
"minLength": 4,
|
||||||
"pattern": "^[a-z0-9_]+$",
|
"pattern": "^[a-z0-9_]+$",
|
||||||
},
|
},
|
||||||
"password": {"type": "string", "maxLength": 20, "minLength": 4},
|
"password": {"type": "string", "minLength": 4},
|
||||||
},
|
},
|
||||||
"required": ["username", "password"],
|
"required": ["username", "password"],
|
||||||
"additionalProperties": False,
|
"additionalProperties": False,
|
||||||
|
@ -80,9 +80,13 @@ class ApiCraftyConfigIndexHandler(BaseApiHandler):
|
|||||||
200,
|
200,
|
||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"data": self.controller.roles.get_all_role_ids()
|
"data": (
|
||||||
if get_only_ids
|
self.controller.roles.get_all_role_ids()
|
||||||
else [model_to_dict(r) for r in self.controller.roles.get_all_roles()],
|
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,
|
200,
|
||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"data": self.controller.roles.get_all_role_ids()
|
"data": (
|
||||||
if get_only_ids
|
self.controller.roles.get_all_role_ids()
|
||||||
else [model_to_dict(r) for r in self.controller.roles.get_all_roles()],
|
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,
|
200,
|
||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"data": self.controller.roles.get_all_role_ids()
|
"data": (
|
||||||
if get_only_ids
|
self.controller.roles.get_all_role_ids()
|
||||||
else [model_to_dict(r) for r in self.controller.roles.get_all_roles()],
|
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,
|
200,
|
||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"data": self.controller.roles.get_all_role_ids()
|
"data": (
|
||||||
if get_only_ids
|
self.controller.roles.get_all_role_ids()
|
||||||
else [model_to_dict(r) for r in self.controller.roles.get_all_roles()],
|
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,
|
200,
|
||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"data": PermissionsServers.get_server_ids_from_role(role_id)
|
"data": (
|
||||||
if get_only_ids
|
PermissionsServers.get_server_ids_from_role(role_id)
|
||||||
else self.controller.roles.get_server_ids_and_perms_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"}
|
405, {"status": "error", "error": "DATA CONSTRAINT FAILED"}
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
new_server_id, new_server_uuid = self.controller.create_api_server(
|
new_server_id = self.controller.create_api_server(data, user["user_id"])
|
||||||
data, user["user_id"]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.controller.servers.stats.record_stats()
|
self.controller.servers.stats.record_stats()
|
||||||
|
|
||||||
@ -734,7 +732,7 @@ class ApiServersIndexHandler(BaseApiHandler):
|
|||||||
(
|
(
|
||||||
f"created server {data['name']}"
|
f"created server {data['name']}"
|
||||||
f" (ID: {new_server_id})"
|
f" (ID: {new_server_id})"
|
||||||
f" (UUID: {new_server_uuid})"
|
f" (UUID: {new_server_id})"
|
||||||
),
|
),
|
||||||
server_id=new_server_id,
|
server_id=new_server_id,
|
||||||
source_ip=self.get_remote_ip(),
|
source_ip=self.get_remote_ip(),
|
||||||
@ -746,7 +744,7 @@ class ApiServersIndexHandler(BaseApiHandler):
|
|||||||
"status": "ok",
|
"status": "ok",
|
||||||
"data": {
|
"data": {
|
||||||
"new_server_id": str(new_server_id),
|
"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.server_permissions import EnumPermissionsServer
|
||||||
from app.classes.models.servers import Servers
|
from app.classes.models.servers import Servers
|
||||||
from app.classes.shared.file_helpers import FileHelpers
|
from app.classes.shared.file_helpers import FileHelpers
|
||||||
from app.classes.shared.helpers import Helpers
|
|
||||||
from app.classes.web.base_api_handler import BaseApiHandler
|
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"})
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
if action == "clone_server":
|
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":
|
if action == "eula":
|
||||||
return self._agree_eula(server_id, auth_data[4]["user_id"])
|
return self._agree_eula(server_id, auth_data[4]["user_id"])
|
||||||
|
|
||||||
@ -60,10 +67,20 @@ class ApiServersServerActionHandler(BaseApiHandler):
|
|||||||
name_counter += 1
|
name_counter += 1
|
||||||
new_server_name = server_data.get("server_name") + f" (Copy {name_counter})"
|
new_server_name = server_data.get("server_name") + f" (Copy {name_counter})"
|
||||||
|
|
||||||
new_server_uuid = Helpers.create_uuid()
|
new_server_id = self.controller.servers.create_server(
|
||||||
while os.path.exists(os.path.join(self.helper.servers_dir, new_server_uuid)):
|
new_server_name,
|
||||||
new_server_uuid = Helpers.create_uuid()
|
None,
|
||||||
new_server_path = os.path.join(self.helper.servers_dir, new_server_uuid)
|
"",
|
||||||
|
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(
|
self.controller.management.add_to_audit_log(
|
||||||
user_id,
|
user_id,
|
||||||
@ -81,19 +98,19 @@ class ApiServersServerActionHandler(BaseApiHandler):
|
|||||||
self.helper.get_os_understandable_path(server_data.get("log_path"))
|
self.helper.get_os_understandable_path(server_data.get("log_path"))
|
||||||
)
|
)
|
||||||
|
|
||||||
new_server_id = self.controller.servers.create_server(
|
server: Servers = self.controller.servers.get_server_obj(new_server_id)
|
||||||
new_server_name,
|
server.path = new_server_path
|
||||||
new_server_uuid,
|
server.log_path = new_server_log_file
|
||||||
new_server_path,
|
server.execution_command = new_server_command
|
||||||
"",
|
self.controller.servers.update_server(server)
|
||||||
new_server_command,
|
|
||||||
server_data.get("executable"),
|
for role in self.controller.server_perms.get_server_roles(server_id):
|
||||||
new_server_log_file,
|
mask = self.controller.server_perms.get_permissions_mask(
|
||||||
server_data.get("stop_command"),
|
role.role_id, server_id
|
||||||
server_data.get("type"),
|
)
|
||||||
user_id,
|
self.controller.server_perms.add_role_server(
|
||||||
server_data.get("server_port"),
|
new_server_id, role.role_id, mask
|
||||||
)
|
)
|
||||||
|
|
||||||
self.controller.servers.init_all_servers()
|
self.controller.servers.init_all_servers()
|
||||||
|
|
||||||
|
@ -145,7 +145,7 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler):
|
|||||||
new_server_id = new_server
|
new_server_id = new_server
|
||||||
new_server = self.controller.servers.get_server_data(new_server)
|
new_server = self.controller.servers.get_server_data(new_server)
|
||||||
self.controller.rename_backup_dir(
|
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
|
# preserve current schedules
|
||||||
for schedule in self.controller.management.get_schedules_by_server(
|
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)
|
self.tasks_manager.remove_all_server_tasks(server_id)
|
||||||
failed = False
|
failed = False
|
||||||
for item in self.controller.servers.failed_servers[:]:
|
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)
|
self.controller.servers.failed_servers.remove(item)
|
||||||
failed = True
|
failed = True
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ def metrics_handlers(handler_args):
|
|||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"/metrics/servers/([0-9]+)/?",
|
r"/metrics/servers/([a-z0-9-]+)/?",
|
||||||
ApiOpenMetricsServersHandler,
|
ApiOpenMetricsServersHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
|
@ -118,15 +118,17 @@ class ServerHandler(BaseHandler):
|
|||||||
"lang_page": Helpers.get_lang_page(
|
"lang_page": Helpers.get_lang_page(
|
||||||
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
|
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
|
||||||
),
|
),
|
||||||
"api_key": {
|
"api_key": (
|
||||||
"name": api_key.name,
|
{
|
||||||
"created": api_key.created,
|
"name": api_key.name,
|
||||||
"server_permissions": api_key.server_permissions,
|
"created": api_key.created,
|
||||||
"crafty_permissions": api_key.crafty_permissions,
|
"server_permissions": api_key.server_permissions,
|
||||||
"superuser": api_key.superuser,
|
"crafty_permissions": api_key.crafty_permissions,
|
||||||
}
|
"superuser": api_key.superuser,
|
||||||
if api_key is not None
|
}
|
||||||
else None,
|
if api_key is not None
|
||||||
|
else None
|
||||||
|
),
|
||||||
"superuser": superuser,
|
"superuser": superuser,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"major": 4,
|
"major": 4,
|
||||||
"minor": 2,
|
"minor": 3,
|
||||||
"sub": 4
|
"sub": 0
|
||||||
}
|
}
|
||||||
|
@ -598,26 +598,30 @@
|
|||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
function send_command(server_id, command) {
|
async function send_command(server_id, command) {
|
||||||
/* this getCookie function is in base.html */
|
/* this getCookie function is in base.html */
|
||||||
const token = getCookie("_xsrf");
|
const token = getCookie("_xsrf");
|
||||||
$.ajax({
|
let res = await fetch(`/api/v2/servers/${server_id}/action/${command}`, {
|
||||||
type: "POST",
|
method: 'POST',
|
||||||
headers: { 'X-XSRFToken': token },
|
headers: {
|
||||||
url: `/api/v2/servers/${server_id}/action/${command}`,
|
'token': token,
|
||||||
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 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']) }} - {{
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||||
data['server_stats']['server_id']['server_name'] }}
|
data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||||
data['server_stats']['server_id']['server_name'] }}
|
data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -454,7 +454,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ($('#backup_path').val() == '') {
|
if ($('#backup_path').val() == '') {
|
||||||
console.log('true')
|
console.log('true')
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||||
data['server_stats']['server_id']['server_name'] }}
|
data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||||
data['server_stats']['server_id']['server_name'] }}
|
data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||||
data['server_stats']['server_id']['server_name'] }}
|
data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||||
data['server_stats']['server_id']['server_name'] }}
|
data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||||
data['server_stats']['server_id']['server_name'] }}
|
data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -235,7 +235,7 @@
|
|||||||
if (key != "start_time" && key != "cron_string" && key != "interval_type") {
|
if (key != "start_time" && key != "cron_string" && key != "interval_type") {
|
||||||
if (typeof value == "boolean") {
|
if (typeof value == "boolean") {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
console.log(key)
|
console.log(key)
|
||||||
if (key === "interval" && value === ""){
|
if (key === "interval" && value === ""){
|
||||||
return 0;
|
return 0;
|
||||||
@ -298,7 +298,7 @@
|
|||||||
title: responseData.status,
|
title: responseData.status,
|
||||||
message: responseData.error
|
message: responseData.error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#schedule_form").on("submit", async function (e) {
|
$("#schedule_form").on("submit", async function (e) {
|
||||||
@ -332,7 +332,7 @@
|
|||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: {
|
headers: {
|
||||||
'X-XSRFToken': token,
|
'X-XSRFToken': token,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: formDataJsonString,
|
body: formDataJsonString,
|
||||||
});
|
});
|
||||||
@ -345,7 +345,7 @@
|
|||||||
title: responseData.error,
|
title: responseData.error,
|
||||||
message: responseData.error_data
|
message: responseData.error_data
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||||
data['server_stats']['server_id']['server_name'] }}
|
data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||||
data['server_stats']['server_id']['server_name'] }}
|
data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||||
data['server_stats']['server_id']['server_name'] }}
|
data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -161,7 +161,7 @@
|
|||||||
if (key != "start_time" && key != "cron_string" && key != "interval_type") {
|
if (key != "start_time" && key != "cron_string" && key != "interval_type") {
|
||||||
if (typeof value == "boolean") {
|
if (typeof value == "boolean") {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
console.log(key)
|
console.log(key)
|
||||||
if (key === "interval" && value === ""){
|
if (key === "interval" && value === ""){
|
||||||
return 0;
|
return 0;
|
||||||
@ -221,7 +221,7 @@
|
|||||||
title: responseData.status,
|
title: responseData.status,
|
||||||
message: responseData.error
|
message: responseData.error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#webhook_form").on("submit", async function (e) {
|
$("#webhook_form").on("submit", async function (e) {
|
||||||
@ -249,7 +249,7 @@
|
|||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: {
|
headers: {
|
||||||
'X-XSRFToken': token,
|
'X-XSRFToken': token,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: formDataJsonString,
|
body: formDataJsonString,
|
||||||
});
|
});
|
||||||
@ -262,15 +262,15 @@
|
|||||||
title: responseData.status,
|
title: responseData.status,
|
||||||
message: responseData.error
|
message: responseData.error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function hexToDiscordInt(hexColor) {
|
function hexToDiscordInt(hexColor) {
|
||||||
// Remove the hash at the start if it's there
|
// Remove the hash at the start if it's there
|
||||||
const sanitizedHex = hexColor.startsWith('#') ? hexColor.slice(1) : hexColor;
|
const sanitizedHex = hexColor.startsWith('#') ? hexColor.slice(1) : hexColor;
|
||||||
|
|
||||||
// Convert the hex to an integer
|
// Convert the hex to an integer
|
||||||
return parseInt(sanitizedHex, 16);
|
return parseInt(sanitizedHex, 16);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||||
data['server_stats']['server_id']['server_name'] }}
|
data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_id'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -77,55 +77,49 @@
|
|||||||
box-shadow: 0 12px 16px 0 hsla(0, 0%, 0%, 0.4);
|
box-shadow: 0 12px 16px 0 hsla(0, 0%, 0%, 0.4);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
{% if data['query'] %}
|
<form id="login-form" data-query="{{ data.get('query', None) }}">
|
||||||
<form action="/login?{{ data['query'] }}" method="post">
|
{% raw xsrf_form_html() %}
|
||||||
{% else %}
|
<div class="form-group">
|
||||||
<form action="/login" method="post">
|
<label class="label">{{ translate('login', 'username', data['lang']) }}</label>
|
||||||
{% end %}
|
<div class="input-group">
|
||||||
{% raw xsrf_form_html() %}
|
<input type="text" class="form-control login-text-input login-input"
|
||||||
<div class="form-group">
|
placeholder="{{ translate('login', 'username', data['lang']) }}" name="username" id="username"
|
||||||
<label class="label">{{ translate('login', 'username', data['lang']) }}</label>
|
required="true">
|
||||||
<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>
|
</div>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<label class="label">{{ translate('login', 'password', data['lang']) }}</label>
|
<div class="form-group">
|
||||||
<div class="input-group">
|
<label class="label">{{ translate('login', 'password', data['lang']) }}</label>
|
||||||
<input type="password" class="form-control login-text-input login-input"
|
<div class="input-group">
|
||||||
placeholder="{{ translate('login', 'password', data['lang']) }}" name="password" id="password"
|
<input type="password" class="form-control login-text-input login-input"
|
||||||
required="true">
|
placeholder="{{ translate('login', 'password', data['lang']) }}" name="password" id="password"
|
||||||
</div>
|
required="true">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<button class="login-input btn btn-primary submit-btn btn-block">{{ translate('login', 'login',
|
<div class="form-group">
|
||||||
data['lang']) }}</button>
|
<button class="login-input btn btn-primary submit-btn btn-block">{{ translate('login', 'login',
|
||||||
</div>
|
data['lang']) }}</button>
|
||||||
{% if error_msg is not None %}
|
</div>
|
||||||
<fieldset style="color: red; text-align: center;">
|
<fieldset id="error-field" style="color: red; text-align: center;">
|
||||||
<span>{{error_msg}}</span>
|
</fieldset>
|
||||||
</fieldset>
|
<div class="form-group d-flex justify-content-between">
|
||||||
{% end %}
|
<div class="form-check form-check-flat mt-0">
|
||||||
<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>
|
||||||
|
<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">
|
<div class="text-block text-center my-3">
|
||||||
<span class="text-small font-weight-semibold"><a href="https://craftycontrol.com/">Crafty Control
|
<span class="text-small font-weight-semibold"><a href="https://craftycontrol.com/">Crafty Control
|
||||||
{{data['version'] }}</a> </span>
|
{{data['version'] }}</a> </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-block text-center my-3">
|
<div class="text-block text-center my-3">
|
||||||
<a href="/status" class="text-small forgot-password">{{ translate('login', 'viewStatus',
|
<a href="/status" class="text-small forgot-password">{{ translate('login', 'viewStatus',
|
||||||
data['lang']) }}</a>
|
data['lang']) }}</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -155,13 +149,13 @@
|
|||||||
document.getElementById('login-form-background').style.background = 'rgb(34, 36, 55, ' + (opacity / 100) + ')';
|
document.getElementById('login-form-background').style.background = 'rgb(34, 36, 55, ' + (opacity / 100) + ')';
|
||||||
//Register Service worker for mobile app
|
//Register Service worker for mobile app
|
||||||
if ('serviceWorker' in navigator) {
|
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) {
|
.then(function (registration) {
|
||||||
console.log('Service Worker Registered');
|
console.log('Service Worker Registered');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
async function resetPass(){
|
async function resetPass() {
|
||||||
let res = await fetch(`/api/v2/crafty/resetPass/`, {
|
let res = await fetch(`/api/v2/crafty/resetPass/`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
@ -170,7 +164,38 @@
|
|||||||
bootbox.alert(responseData.data)
|
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>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.modal-content {
|
.modal-content {
|
||||||
|
@ -556,7 +556,7 @@
|
|||||||
xmlHttpRequest.addEventListener('load', (event) => {
|
xmlHttpRequest.addEventListener('load', (event) => {
|
||||||
if (event.target.responseText == 'success') {
|
if (event.target.responseText == 'success') {
|
||||||
console.log('Upload for file', file.name, 'was successful!')
|
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";
|
document.getElementById("lower_half").style.visibility = "visible";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -881,7 +881,7 @@
|
|||||||
xmlHttpRequest.addEventListener('load', (event) => {
|
xmlHttpRequest.addEventListener('load', (event) => {
|
||||||
if (event.target.responseText == 'success') {
|
if (event.target.responseText == 'success') {
|
||||||
console.log('Upload for file', file.name, 'was successful!')
|
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").style.visibility = "visible";
|
||||||
document.getElementById("lower_half").hidden = false;
|
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"
|
"version": "Version"
|
||||||
},
|
},
|
||||||
"login": {
|
"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",
|
"forgotPassword": "Passwort vergessen",
|
||||||
|
"incorrect": "Benutzername oder Passwort falsch",
|
||||||
"login": "Einloggen",
|
"login": "Einloggen",
|
||||||
"password": "Passwort",
|
"password": "Passwort",
|
||||||
"username": "Nutzername",
|
"username": "Nutzername",
|
||||||
|
@ -215,7 +215,10 @@
|
|||||||
"version": "Version"
|
"version": "Version"
|
||||||
},
|
},
|
||||||
"login": {
|
"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",
|
"forgotPassword": "Forgot Password",
|
||||||
|
"incorrect": "Incorrect username or password",
|
||||||
"login": "Log In",
|
"login": "Log In",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"username": "Username",
|
"username": "Username",
|
||||||
|
@ -111,6 +111,7 @@
|
|||||||
"starting": "Inicio-retrasado",
|
"starting": "Inicio-retrasado",
|
||||||
"status": "Estado",
|
"status": "Estado",
|
||||||
"stop": "Detener",
|
"stop": "Detener",
|
||||||
|
"storage": "Almacenamiento",
|
||||||
"version": "Versión",
|
"version": "Versión",
|
||||||
"welcome": "Bienvenido a Crafty Controller"
|
"welcome": "Bienvenido a Crafty Controller"
|
||||||
},
|
},
|
||||||
@ -214,7 +215,10 @@
|
|||||||
"version": "Versión"
|
"version": "Versión"
|
||||||
},
|
},
|
||||||
"login": {
|
"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",
|
"forgotPassword": "Olvidé mi contraseña",
|
||||||
|
"incorrect": "El nombre de usuario o contraseña es incorrecto",
|
||||||
"login": "Iniciar Sesión",
|
"login": "Iniciar Sesión",
|
||||||
"password": "Contraseña",
|
"password": "Contraseña",
|
||||||
"username": "Usuario",
|
"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.",
|
"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.",
|
"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",
|
"cancel": "Cancelar",
|
||||||
|
"countPlayers": "Incluir el servidor en la cuenta total de jugadores",
|
||||||
"crashTime": "Tiempo de espera por crasheo",
|
"crashTime": "Tiempo de espera por crasheo",
|
||||||
"crashTimeDesc": "¿Cuanto tiempo esperar para considerar el servidor como crasheado?",
|
"crashTimeDesc": "¿Cuanto tiempo esperar para considerar el servidor como crasheado?",
|
||||||
"deleteFilesQuestion": "¿Eliminar archivos del servidor del host?",
|
"deleteFilesQuestion": "¿Eliminar archivos del servidor del host?",
|
||||||
@ -510,6 +515,7 @@
|
|||||||
"cpuUsage": "Uso de CPU",
|
"cpuUsage": "Uso de CPU",
|
||||||
"description": "Descripción",
|
"description": "Descripción",
|
||||||
"errorCalculatingUptime": "Error calculando tiempo de actividad",
|
"errorCalculatingUptime": "Error calculando tiempo de actividad",
|
||||||
|
"loadingMotd": "Cargando MOTD",
|
||||||
"memUsage": "Uso de memoria",
|
"memUsage": "Uso de memoria",
|
||||||
"offline": "Desconectado",
|
"offline": "Desconectado",
|
||||||
"online": "En línea",
|
"online": "En línea",
|
||||||
@ -577,6 +583,7 @@
|
|||||||
"serverUpload": "Subir servidor comprimido",
|
"serverUpload": "Subir servidor comprimido",
|
||||||
"serverVersion": "Versión del servidor",
|
"serverVersion": "Versión del servidor",
|
||||||
"sizeInGB": "Tamaño en GB",
|
"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",
|
"uploadButton": "Subir",
|
||||||
"uploadZip": "Subir archivo Zip para importar servidor",
|
"uploadZip": "Subir archivo Zip para importar servidor",
|
||||||
"zipPath": "Ruta del servidor"
|
"zipPath": "Ruta del servidor"
|
||||||
@ -591,6 +598,15 @@
|
|||||||
"newServer": "Crear nuevo Servidor",
|
"newServer": "Crear nuevo Servidor",
|
||||||
"servers": "Servidores"
|
"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": {
|
"userConfig": {
|
||||||
"apiKey": "Claves API",
|
"apiKey": "Claves API",
|
||||||
"auth": "¿Autorizado? ",
|
"auth": "¿Autorizado? ",
|
@ -215,7 +215,10 @@
|
|||||||
"version": "Version"
|
"version": "Version"
|
||||||
},
|
},
|
||||||
"login": {
|
"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é",
|
"forgotPassword": "Mot de Passe Oublié",
|
||||||
|
"incorrect": "Identifiant et/ou mot de passe incorrect.",
|
||||||
"login": "Connexion",
|
"login": "Connexion",
|
||||||
"password": "Mot de Passe",
|
"password": "Mot de Passe",
|
||||||
"username": "Nom d'Utilisateur",
|
"username": "Nom d'Utilisateur",
|
||||||
|
@ -215,7 +215,10 @@
|
|||||||
"version": "גרסה"
|
"version": "גרסה"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
|
"defaultPath": "הסיסמה שהזנת היא נתיב האישורים המוגדר כברירת מחדל, ולא הסיסמה עצמה. אנא מצא את הסיסמה המוגדרת כברירת מחדל במיקום זה.",
|
||||||
|
"disabled": "חשבון המשתמש מושבת. אנא פנה למנהל המערכת שלך לקבלת מידע נוסף.",
|
||||||
"forgotPassword": "שכחתי סיסמה",
|
"forgotPassword": "שכחתי סיסמה",
|
||||||
|
"incorrect": "שם משתמש או סיסמה שגויים",
|
||||||
"login": "התחברות",
|
"login": "התחברות",
|
||||||
"password": "סיסמה",
|
"password": "סיסמה",
|
||||||
"username": "שם משתמש",
|
"username": "שם משתמש",
|
||||||
|
@ -215,7 +215,10 @@
|
|||||||
"version": "VERSHUN"
|
"version": "VERSHUN"
|
||||||
},
|
},
|
||||||
"login": {
|
"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",
|
"forgotPassword": "FORGWOTS YOUR SEEKRET",
|
||||||
|
"incorrect": "U gotz wrong name or passwurd",
|
||||||
"login": "WOG INZ",
|
"login": "WOG INZ",
|
||||||
"password": "SEEKRET",
|
"password": "SEEKRET",
|
||||||
"username": "USERNAEM",
|
"username": "USERNAEM",
|
||||||
|
@ -216,7 +216,10 @@
|
|||||||
"version": "Versija"
|
"version": "Versija"
|
||||||
},
|
},
|
||||||
"login": {
|
"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",
|
"forgotPassword": "Aizmirsu Paroli",
|
||||||
|
"incorrect": "Nepareizs lietotājvārds vai parole",
|
||||||
"login": "Ieiet",
|
"login": "Ieiet",
|
||||||
"password": "Parole",
|
"password": "Parole",
|
||||||
"username": "Lietotājvārds",
|
"username": "Lietotājvārds",
|
||||||
|
@ -215,7 +215,10 @@
|
|||||||
"version": "Versie"
|
"version": "Versie"
|
||||||
},
|
},
|
||||||
"login": {
|
"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",
|
"forgotPassword": "Wachtwoord vergeten",
|
||||||
|
"incorrect": "Verkeerde gebruikersnaam of wachtwoord",
|
||||||
"login": "Log In",
|
"login": "Log In",
|
||||||
"password": "Wachtwoord",
|
"password": "Wachtwoord",
|
||||||
"username": "gebruikersnaam",
|
"username": "gebruikersnaam",
|
||||||
|
@ -215,7 +215,10 @@
|
|||||||
"version": "Wersja"
|
"version": "Wersja"
|
||||||
},
|
},
|
||||||
"login": {
|
"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",
|
"forgotPassword": "Zapomniałem hasła",
|
||||||
|
"incorrect": "Niepoprawny login lub hasło/Niepoprawna nazwa użytkownika lub hasło",
|
||||||
"login": "Zaloguj się",
|
"login": "Zaloguj się",
|
||||||
"password": "Hasło",
|
"password": "Hasło",
|
||||||
"username": "Nazwa użytkownika",
|
"username": "Nazwa użytkownika",
|
||||||
|
@ -215,7 +215,10 @@
|
|||||||
"version": "เวอร์ชั่น"
|
"version": "เวอร์ชั่น"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
|
"defaultPath": "รหัสผ่านที่คุณกรอกคือเส้นทางข้อมูลเริ่มต้น ไม่ใช่รหัสผ่าน กรุณาค้นหารหัสผ่านเริ่มต้นในตำแหน่งนั้น",
|
||||||
|
"disabled": "บัญชีผู้ใช้ถูกปิดใช้งาน กรุณาติดต่อผู้ดูแลระบบของคุณสำหรับข้อมูลเพิ่มเติม",
|
||||||
"forgotPassword": "ลืมรหัสผ่าน",
|
"forgotPassword": "ลืมรหัสผ่าน",
|
||||||
|
"incorrect": "ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง",
|
||||||
"login": "เข้าสู่ระบบ",
|
"login": "เข้าสู่ระบบ",
|
||||||
"password": "รหัสผ่าน",
|
"password": "รหัสผ่าน",
|
||||||
"username": "ชื่อผู้ใช้",
|
"username": "ชื่อผู้ใช้",
|
||||||
|
@ -215,7 +215,10 @@
|
|||||||
"version": "Sürüm"
|
"version": "Sürüm"
|
||||||
},
|
},
|
||||||
"login": {
|
"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",
|
"forgotPassword": "Şifremi Unuttum",
|
||||||
|
"incorrect": "Kullanıcı adınız veya şifreniz yanlış.",
|
||||||
"login": "Oturum Aç",
|
"login": "Oturum Aç",
|
||||||
"password": "Şifre",
|
"password": "Şifre",
|
||||||
"username": "Kullanıcı Adı",
|
"username": "Kullanıcı Adı",
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
"cpuCurFreq": "Швидкість CPU",
|
"cpuCurFreq": "Швидкість CPU",
|
||||||
"cpuMaxFreq": "Максимальна швидкість CPU",
|
"cpuMaxFreq": "Максимальна швидкість CPU",
|
||||||
"cpuUsage": "Використання CPU",
|
"cpuUsage": "Використання CPU",
|
||||||
"crashed": "Аварійне завершення",
|
"crashed": "Краш",
|
||||||
"dashboard": "Панель",
|
"dashboard": "Панель",
|
||||||
"delay-explained": "Служба/агент нещодавно запущено та затримує запуск серверів minecraft",
|
"delay-explained": "Служба/агент нещодавно запущено та затримує запуск серверів minecraft",
|
||||||
"host": "Хост",
|
"host": "Хост",
|
||||||
@ -215,7 +215,10 @@
|
|||||||
"version": "Версія"
|
"version": "Версія"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
|
"defaultPath": "Пароль, який ви ввели, є шляхом до облікових даних за умовчанням, а не паролем. Будь ласка, знайдіть стандартний пароль у цьому місці.",
|
||||||
|
"disabled": "Користувача вимкнено. Зверніться до вашого системного адміністратора за допомогою.",
|
||||||
"forgotPassword": "Забули пароль",
|
"forgotPassword": "Забули пароль",
|
||||||
|
"incorrect": "Неправильний логін або пароль",
|
||||||
"login": "Вхід",
|
"login": "Вхід",
|
||||||
"password": "Пароль",
|
"password": "Пароль",
|
||||||
"username": "Логін",
|
"username": "Логін",
|
||||||
@ -351,7 +354,7 @@
|
|||||||
"sendingRequest": "Надсилання вашого запиту...",
|
"sendingRequest": "Надсилання вашого запиту...",
|
||||||
"serverAutoStart": "Сервер Авто-старт",
|
"serverAutoStart": "Сервер Авто-старт",
|
||||||
"serverAutostartDelay": "Сервер Авто-старт затримка",
|
"serverAutostartDelay": "Сервер Авто-старт затримка",
|
||||||
"serverAutostartDelayDesc": "Затримка Авто-старту сервера (Якщо увімкнуто раніше)",
|
"serverAutostartDelayDesc": "Затримка Авто-старту сервера (Після запуску Crafty))",
|
||||||
"serverCrashDetection": "Детектор крашу сервера",
|
"serverCrashDetection": "Детектор крашу сервера",
|
||||||
"serverExecutable": "Виконуваний файл Серверу",
|
"serverExecutable": "Виконуваний файл Серверу",
|
||||||
"serverExecutableDesc": "Це виконуваний файл для запуску сервера",
|
"serverExecutableDesc": "Це виконуваний файл для запуску сервера",
|
||||||
@ -369,7 +372,7 @@
|
|||||||
"serverPortDesc": "Цей порт призначений для статистики Crafty",
|
"serverPortDesc": "Цей порт призначений для статистики Crafty",
|
||||||
"serverStopCommand": "Команда зупинки сервера",
|
"serverStopCommand": "Команда зупинки сервера",
|
||||||
"serverStopCommandDesc": "Команда яка буде надсилатись, щоб зупинити сервер",
|
"serverStopCommandDesc": "Команда яка буде надсилатись, щоб зупинити сервер",
|
||||||
"showStatus": "Показувати на публічній сторінці статус",
|
"showStatus": "Показувати статус на публічній сторінці",
|
||||||
"shutdownTimeout": "Час відклику зупинки",
|
"shutdownTimeout": "Час відклику зупинки",
|
||||||
"statsHint1": "Цей порт на якому працює сервер. Це потрібно лиш для того щоб Crafty міг виводити статистику для цього сервера.",
|
"statsHint1": "Цей порт на якому працює сервер. Це потрібно лиш для того щоб Crafty міг виводити статистику для цього сервера.",
|
||||||
"statsHint2": "Це не змінює порт вашого сервера. Ви мусите власноруч змінити налаштування в server.properties або іншому конфігураційному файлі.",
|
"statsHint2": "Це не змінює порт вашого сервера. Ви мусите власноруч змінити налаштування в server.properties або іншому конфігураційному файлі.",
|
||||||
@ -406,7 +409,7 @@
|
|||||||
"logs": "Логи",
|
"logs": "Логи",
|
||||||
"metrics": "Графік",
|
"metrics": "Графік",
|
||||||
"playerControls": "Керування Гравцями",
|
"playerControls": "Керування Гравцями",
|
||||||
"reset": "Повернутись нагору",
|
"reset": "Вниз",
|
||||||
"schedule": "Розклад",
|
"schedule": "Розклад",
|
||||||
"serverDetails": "Деталі сервера",
|
"serverDetails": "Деталі сервера",
|
||||||
"terminal": "Термінал"
|
"terminal": "Термінал"
|
||||||
|
@ -215,7 +215,10 @@
|
|||||||
"version": "版本"
|
"version": "版本"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
|
"defaultPath": "您输入的密码是默认凭据的路径,不是其中的密码。请在此路径中找到默认密码。",
|
||||||
|
"disabled": "用户账号已禁用。请联系您的系统管理员以了解更多信息。",
|
||||||
"forgotPassword": "忘记密码",
|
"forgotPassword": "忘记密码",
|
||||||
|
"incorrect": "用户名或密码错误",
|
||||||
"login": "登录",
|
"login": "登录",
|
||||||
"password": "密码",
|
"password": "密码",
|
||||||
"username": "用户名",
|
"username": "用户名",
|
||||||
|
@ -4,13 +4,13 @@ argon2-cffi==23.1.0
|
|||||||
cached_property==1.5.2
|
cached_property==1.5.2
|
||||||
colorama==0.4.6
|
colorama==0.4.6
|
||||||
croniter==1.4.1
|
croniter==1.4.1
|
||||||
cryptography==41.0.7
|
cryptography==42.0.4
|
||||||
libgravatar==1.0.4
|
libgravatar==1.0.4
|
||||||
nh3==0.2.14
|
nh3==0.2.14
|
||||||
packaging==23.2
|
packaging==23.2
|
||||||
peewee==3.13
|
peewee==3.13
|
||||||
psutil==5.9.5
|
psutil==5.9.5
|
||||||
pyOpenSSL==23.3.0
|
pyOpenSSL==24.0.0
|
||||||
pyjwt==2.8.0
|
pyjwt==2.8.0
|
||||||
PyYAML==6.0.1
|
PyYAML==6.0.1
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
|
@ -3,7 +3,7 @@ sonar.organization=crafty-controller
|
|||||||
|
|
||||||
# This is the name and version displayed in the SonarCloud UI.
|
# This is the name and version displayed in the SonarCloud UI.
|
||||||
sonar.projectName=Crafty 4
|
sonar.projectName=Crafty 4
|
||||||
sonar.projectVersion=4.2.4
|
sonar.projectVersion=4.3.0
|
||||||
sonar.python.version=3.9, 3.10, 3.11
|
sonar.python.version=3.9, 3.10, 3.11
|
||||||
sonar.exclusions=app/migrations/**, app/frontend/static/assets/vendors/**
|
sonar.exclusions=app/migrations/**, app/frontend/static/assets/vendors/**
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user