mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'dev' into 'master'
4.0.4 See merge request crafty-controller/crafty-4!379
This commit is contained in:
commit
abeec0871c
@ -49,6 +49,7 @@ win-prod-build:
|
||||
paths:
|
||||
- .venv/
|
||||
rules:
|
||||
- if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
|
||||
- if: $CI_COMMIT_TAG
|
||||
environment:
|
||||
name: production
|
||||
|
42
CHANGELOG.md
42
CHANGELOG.md
@ -1,47 +1,53 @@
|
||||
# Changelog
|
||||
|
||||
## [4.0.3] - 2022/06/18
|
||||
## --- [4.0.4] - 2022/06/21
|
||||
|
||||
### New features
|
||||
- Integrate Wiki iframe into panel instead of link ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/367))
|
||||
- Add shutdown on backup feature ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/373))
|
||||
- Add detection and dropdown of java versions ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/375))
|
||||
- Add file-editor size toggle ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/378))
|
||||
### Bug fixes
|
||||
- Backup/Config.json rework for API key hardening ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/369))
|
||||
- Fix stack on ping result being falsy ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/371))
|
||||
- Fix sec bug with server creation roles ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/376))
|
||||
### Tweaks
|
||||
- Spelling mistake fixed in German lang file ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/370))
|
||||
- Backup failure warning (Tab text goes red) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/373))
|
||||
- - ([Merge Request 2](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/377))
|
||||
- Rework server list on dashboard display for use on small screens ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/372))
|
||||
- File handling enhancements ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/362))
|
||||
<br><br>
|
||||
|
||||
## --- [4.0.3] - 2022/06/18
|
||||
### New features
|
||||
- Integrate Wiki iframe into panel instead of link ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/367))
|
||||
### Bug fixes
|
||||
- Amend Java system variable fix to be more specfic since they only affect Oracle. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/364))
|
||||
- API Token authentication hardening ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/364))
|
||||
### Tweaks
|
||||
- Add better error logging for statistic collection ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/359))
|
||||
<br><br>
|
||||
|
||||
## [4.0.2-hotfix1] - 2022/06/17
|
||||
|
||||
## --- [4.0.2-hotfix1] - 2022/06/17
|
||||
### Crit Bug fixes
|
||||
- Fix blank server_detail page for general users ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/358))
|
||||
<br><br>
|
||||
|
||||
## [4.0.2] - 2022/06/16
|
||||
|
||||
## --- [4.0.2] - 2022/06/16
|
||||
### New features
|
||||
None
|
||||
|
||||
### Bug fixes
|
||||
- Fix winreg import pass on non-NT systems ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/344))
|
||||
- Make the WebSocket automatically reconnect. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/345))
|
||||
- Fix an error when there are no servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/346))
|
||||
- Use relative paths for the jarfile and logs ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/347))
|
||||
- Flatten all instances of username creation or editing, usernames should be lower case.
|
||||
- - ([Merge Request 1](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/342))
|
||||
- - ([Merge Request 2](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/351))
|
||||
- Add version inheretence & config check ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/353))
|
||||
- Fix support log temp file deletion issue/hang ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/354))
|
||||
<br><br>
|
||||
|
||||
## [4.0.1] - 2022/06/15
|
||||
|
||||
## --- [4.0.1] - 2022/06/15
|
||||
### New features
|
||||
None
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Remove session.lock warning ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/338))
|
||||
- Correct Dutch Spacing Issue ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/340))
|
||||
- Remove no-else-* pylint exemptions and tidy code. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/342))
|
||||
- Make unRAID more readable, and flatten path to lower, to fit standard practice. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/337))
|
||||
- Fix Java Pathing issues on windows ([Commit](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/343/diffs?commit_id=cda2120579083d447db5dbeb5489822880f4cae7))
|
||||
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
|
||||
[![Supported Python Versions](https://shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20-blue)](https://www.python.org)
|
||||
[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.3--beta-orange)](https://gitlab.com/crafty-controller/crafty-4/-/releases)
|
||||
[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.4--beta-orange)](https://gitlab.com/crafty-controller/crafty-4/-/releases)
|
||||
[![Code Quality(temp-hardcoded)](https://img.shields.io/badge/code%20quality-10-brightgreen)](https://gitlab.com/crafty-controller/crafty-4)
|
||||
[![Build Status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master)
|
||||
|
||||
# Crafty Controller 4.0.3-beta
|
||||
# Crafty Controller 4.0.4-beta
|
||||
> Python based Control Panel for your Minecraft Server
|
||||
|
||||
## What is Crafty Controller?
|
||||
|
@ -17,6 +17,14 @@ class ManagementController:
|
||||
def get_latest_hosts_stats():
|
||||
return HelpersManagement.get_latest_hosts_stats()
|
||||
|
||||
@staticmethod
|
||||
def set_crafty_api_key(key):
|
||||
HelpersManagement.set_secret_api_key(key)
|
||||
|
||||
@staticmethod
|
||||
def get_crafty_api_key():
|
||||
return HelpersManagement.get_secret_api_key()
|
||||
|
||||
# **********************************************************************************
|
||||
# Commands Methods
|
||||
# **********************************************************************************
|
||||
@ -128,9 +136,10 @@ class ManagementController:
|
||||
max_backups: int = None,
|
||||
excluded_dirs: list = None,
|
||||
compress: bool = False,
|
||||
shutdown: bool = False,
|
||||
):
|
||||
return self.management_helper.set_backup_config(
|
||||
server_id, backup_path, max_backups, excluded_dirs, compress
|
||||
server_id, backup_path, max_backups, excluded_dirs, compress, shutdown
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
@ -5,6 +5,7 @@ import json
|
||||
import typing as t
|
||||
|
||||
from app.classes.controllers.roles_controller import RolesController
|
||||
from app.classes.shared.file_helpers import FileHelpers
|
||||
|
||||
from app.classes.shared.singleton import Singleton
|
||||
from app.classes.shared.server import ServerInstance
|
||||
@ -28,8 +29,9 @@ logger = logging.getLogger(__name__)
|
||||
class ServersController(metaclass=Singleton):
|
||||
servers_list: ServerInstance
|
||||
|
||||
def __init__(self, helper, servers_helper, management_helper):
|
||||
def __init__(self, helper, servers_helper, management_helper, file_helper):
|
||||
self.helper: Helpers = helper
|
||||
self.file_helper: FileHelpers = file_helper
|
||||
self.servers_helper: HelperServers = servers_helper
|
||||
self.management_helper = management_helper
|
||||
self.servers_list = []
|
||||
@ -189,6 +191,7 @@ class ServersController(metaclass=Singleton):
|
||||
self.helper,
|
||||
self.management_helper,
|
||||
self.stats,
|
||||
self.file_helper,
|
||||
),
|
||||
"server_settings": settings.props,
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ class Stats:
|
||||
|
||||
return level_total_size
|
||||
|
||||
def get_server_players(self, server_id): # pylint: disable=no-self-use
|
||||
def get_server_players(self, server_id):
|
||||
|
||||
server = HelperServers.get_server_data_by_id(server_id)
|
||||
|
||||
@ -265,15 +265,24 @@ class Stats:
|
||||
logger.info(
|
||||
"Unable to read the server icon due to the following error:", exc_info=e
|
||||
)
|
||||
|
||||
ping_data = {
|
||||
"online": online_stats.get("online", 0),
|
||||
"max": online_stats.get("max", 0),
|
||||
"players": online_stats.get("players", 0),
|
||||
"server_description": ping_obj.description,
|
||||
"server_version": ping_obj.version,
|
||||
"server_icon": server_icon,
|
||||
}
|
||||
if ping_obj:
|
||||
ping_data = {
|
||||
"online": online_stats.get("online", 0),
|
||||
"max": online_stats.get("max", 0),
|
||||
"players": online_stats.get("players", 0),
|
||||
"server_description": ping_obj.description,
|
||||
"server_version": ping_obj.version,
|
||||
"server_icon": server_icon,
|
||||
}
|
||||
else:
|
||||
ping_data = {
|
||||
"online": online_stats.get("online", 0),
|
||||
"max": online_stats.get("max", 0),
|
||||
"players": online_stats.get("players", 0),
|
||||
"server_description": "",
|
||||
"server_version": "",
|
||||
"server_icon": server_icon,
|
||||
}
|
||||
|
||||
return ping_data
|
||||
|
||||
|
@ -38,6 +38,16 @@ class AuditLog(BaseModel):
|
||||
table_name = "audit_log"
|
||||
|
||||
|
||||
# **********************************************************************************
|
||||
# Crafty Settings Class
|
||||
# **********************************************************************************
|
||||
class CraftySettings(BaseModel):
|
||||
secret_api_key = CharField(default="")
|
||||
|
||||
class Meta:
|
||||
table_name = "crafty_settings"
|
||||
|
||||
|
||||
# **********************************************************************************
|
||||
# Host_Stats Class
|
||||
# **********************************************************************************
|
||||
@ -118,6 +128,7 @@ class Backups(BaseModel):
|
||||
max_backups = IntegerField()
|
||||
server_id = ForeignKeyField(Servers, backref="backups_server")
|
||||
compress = BooleanField(default=False)
|
||||
shutdown = BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
table_name = "backups"
|
||||
@ -231,6 +242,17 @@ class HelpersManagement:
|
||||
else:
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def set_secret_api_key(key):
|
||||
CraftySettings.insert(secret_api_key=key).execute()
|
||||
|
||||
@staticmethod
|
||||
def get_secret_api_key():
|
||||
settings = CraftySettings.select(CraftySettings.secret_api_key).where(
|
||||
CraftySettings.id == 1
|
||||
)
|
||||
return settings[0].secret_api_key
|
||||
|
||||
# **********************************************************************************
|
||||
# Schedules Methods
|
||||
# **********************************************************************************
|
||||
@ -330,6 +352,7 @@ class HelpersManagement:
|
||||
"max_backups": row.max_backups,
|
||||
"server_id": row.server_id_id,
|
||||
"compress": row.compress,
|
||||
"shutdown": row.shutdown,
|
||||
}
|
||||
except IndexError:
|
||||
conf = {
|
||||
@ -338,6 +361,7 @@ class HelpersManagement:
|
||||
"max_backups": 0,
|
||||
"server_id": server_id,
|
||||
"compress": False,
|
||||
"shutdown": False,
|
||||
}
|
||||
return conf
|
||||
|
||||
@ -348,6 +372,7 @@ class HelpersManagement:
|
||||
max_backups: int = None,
|
||||
excluded_dirs: list = None,
|
||||
compress: bool = False,
|
||||
shutdown: bool = False,
|
||||
):
|
||||
logger.debug(f"Updating server {server_id} backup config with {locals()}")
|
||||
if Backups.select().where(Backups.server_id == server_id).exists():
|
||||
@ -359,6 +384,7 @@ class HelpersManagement:
|
||||
"max_backups": 0,
|
||||
"server_id": server_id,
|
||||
"compress": False,
|
||||
"shutdown": False,
|
||||
}
|
||||
new_row = True
|
||||
if max_backups is not None:
|
||||
@ -367,6 +393,7 @@ class HelpersManagement:
|
||||
dirs_to_exclude = ",".join(excluded_dirs)
|
||||
conf["excluded_dirs"] = dirs_to_exclude
|
||||
conf["compress"] = compress
|
||||
conf["shutdown"] = shutdown
|
||||
if not new_row:
|
||||
with self.database.atomic():
|
||||
if backup_path is not None:
|
||||
|
@ -5,6 +5,7 @@ import jwt
|
||||
from jwt import PyJWTError
|
||||
|
||||
from app.classes.models.users import HelperUsers, ApiKeys
|
||||
from app.classes.controllers.management_controller import ManagementController
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -13,11 +14,14 @@ class Authentication:
|
||||
def __init__(self, helper):
|
||||
self.helper = helper
|
||||
self.secret = "my secret"
|
||||
self.secret = self.helper.get_setting("apikey_secret", None)
|
||||
|
||||
if self.secret is None or self.secret == "random":
|
||||
try:
|
||||
self.secret = ManagementController.get_crafty_api_key()
|
||||
if self.secret == "":
|
||||
self.secret = self.helper.random_string_generator(64)
|
||||
ManagementController.set_crafty_api_key(str(self.secret))
|
||||
except:
|
||||
self.secret = self.helper.random_string_generator(64)
|
||||
self.helper.set_setting("apikey_secret", self.secret)
|
||||
ManagementController.set_crafty_api_key(str(self.secret))
|
||||
|
||||
def generate(self, user_id, extra=None):
|
||||
if extra is None:
|
||||
|
@ -2,14 +2,22 @@ import os
|
||||
import shutil
|
||||
import logging
|
||||
import pathlib
|
||||
import tempfile
|
||||
import zipfile
|
||||
from zipfile import ZipFile, ZIP_DEFLATED
|
||||
|
||||
from app.classes.shared.helpers import Helpers
|
||||
from app.classes.shared.console import Console
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FileHelpers:
|
||||
allowed_quotes = ['"', "'", "`"]
|
||||
|
||||
def __init__(self, helper):
|
||||
self.helper: Helpers = helper
|
||||
|
||||
@staticmethod
|
||||
def del_dirs(path):
|
||||
path = pathlib.Path(path)
|
||||
@ -82,7 +90,6 @@ class FileHelpers:
|
||||
f"Error backing up: {os.path.join(root, file)}!"
|
||||
f" - Error was: {e}"
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
@ -113,3 +120,173 @@ class FileHelpers:
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
def make_compressed_backup(
|
||||
self, path_to_destination, path_to_zip, excluded_dirs, server_id
|
||||
):
|
||||
# create a ZipFile object
|
||||
path_to_destination += ".zip"
|
||||
ex_replace = [p.replace("\\", "/") for p in excluded_dirs]
|
||||
total_bytes = 0
|
||||
dir_bytes = Helpers.get_dir_size(path_to_zip)
|
||||
results = {
|
||||
"percent": 0,
|
||||
"total_files": self.helper.human_readable_file_size(dir_bytes),
|
||||
}
|
||||
self.helper.websocket_helper.broadcast_page_params(
|
||||
"/panel/server_detail",
|
||||
{"id": str(server_id)},
|
||||
"backup_status",
|
||||
results,
|
||||
)
|
||||
with ZipFile(path_to_destination, "w", ZIP_DEFLATED) as zip_file:
|
||||
for root, dirs, files in os.walk(path_to_zip, topdown=True):
|
||||
for l_dir in dirs:
|
||||
if str(os.path.join(root, l_dir)).replace("\\", "/") in ex_replace:
|
||||
dirs.remove(l_dir)
|
||||
ziproot = path_to_zip
|
||||
for file in files:
|
||||
if (
|
||||
str(os.path.join(root, file)).replace("\\", "/")
|
||||
not in ex_replace
|
||||
and file != "crafty.sqlite"
|
||||
):
|
||||
try:
|
||||
logger.info(f"backing up: {os.path.join(root, file)}")
|
||||
if os.name == "nt":
|
||||
zip_file.write(
|
||||
os.path.join(root, file),
|
||||
os.path.join(root.replace(ziproot, ""), file),
|
||||
)
|
||||
else:
|
||||
zip_file.write(
|
||||
os.path.join(root, file),
|
||||
os.path.join(root.replace(ziproot, "/"), file),
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f"Error backing up: {os.path.join(root, file)}!"
|
||||
f" - Error was: {e}"
|
||||
)
|
||||
total_bytes += os.path.getsize(os.path.join(root, file))
|
||||
percent = round((total_bytes / dir_bytes) * 100, 2)
|
||||
results = {
|
||||
"percent": percent,
|
||||
"total_files": self.helper.human_readable_file_size(dir_bytes),
|
||||
}
|
||||
self.helper.websocket_helper.broadcast_page_params(
|
||||
"/panel/server_detail",
|
||||
{"id": str(server_id)},
|
||||
"backup_status",
|
||||
results,
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
def make_backup(self, path_to_destination, path_to_zip, excluded_dirs, server_id):
|
||||
# create a ZipFile object
|
||||
path_to_destination += ".zip"
|
||||
ex_replace = [p.replace("\\", "/") for p in excluded_dirs]
|
||||
total_bytes = 0
|
||||
dir_bytes = Helpers.get_dir_size(path_to_zip)
|
||||
results = {
|
||||
"percent": 0,
|
||||
"total_files": self.helper.human_readable_file_size(dir_bytes),
|
||||
}
|
||||
self.helper.websocket_helper.broadcast_page_params(
|
||||
"/panel/server_detail",
|
||||
{"id": str(server_id)},
|
||||
"backup_status",
|
||||
results,
|
||||
)
|
||||
with ZipFile(path_to_destination, "w") as zip_file:
|
||||
for root, dirs, files in os.walk(path_to_zip, topdown=True):
|
||||
for l_dir in dirs:
|
||||
if str(os.path.join(root, l_dir)).replace("\\", "/") in ex_replace:
|
||||
dirs.remove(l_dir)
|
||||
ziproot = path_to_zip
|
||||
for file in files:
|
||||
if (
|
||||
str(os.path.join(root, file)).replace("\\", "/")
|
||||
not in ex_replace
|
||||
and file != "crafty.sqlite"
|
||||
):
|
||||
try:
|
||||
logger.info(f"backing up: {os.path.join(root, file)}")
|
||||
if os.name == "nt":
|
||||
zip_file.write(
|
||||
os.path.join(root, file),
|
||||
os.path.join(root.replace(ziproot, ""), file),
|
||||
)
|
||||
else:
|
||||
zip_file.write(
|
||||
os.path.join(root, file),
|
||||
os.path.join(root.replace(ziproot, "/"), file),
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f"Error backing up: {os.path.join(root, file)}!"
|
||||
f" - Error was: {e}"
|
||||
)
|
||||
total_bytes += os.path.getsize(os.path.join(root, file))
|
||||
percent = round((total_bytes / dir_bytes) * 100, 2)
|
||||
results = {
|
||||
"percent": percent,
|
||||
"total_files": self.helper.human_readable_file_size(dir_bytes),
|
||||
}
|
||||
self.helper.websocket_helper.broadcast_page_params(
|
||||
"/panel/server_detail",
|
||||
{"id": str(server_id)},
|
||||
"backup_status",
|
||||
results,
|
||||
)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def unzip_file(zip_path):
|
||||
new_dir_list = zip_path.split("/")
|
||||
new_dir = ""
|
||||
for i in range(len(new_dir_list) - 1):
|
||||
if i == 0:
|
||||
new_dir += new_dir_list[i]
|
||||
else:
|
||||
new_dir += "/" + new_dir_list[i]
|
||||
|
||||
if Helpers.check_file_perms(zip_path) and os.path.isfile(zip_path):
|
||||
Helpers.ensure_dir_exists(new_dir)
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
try:
|
||||
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
||||
zip_ref.extractall(temp_dir)
|
||||
for i in enumerate(zip_ref.filelist):
|
||||
if len(zip_ref.filelist) > 1 or not zip_ref.filelist[
|
||||
i
|
||||
].filename.endswith("/"):
|
||||
break
|
||||
|
||||
full_root_path = temp_dir
|
||||
|
||||
for item in os.listdir(full_root_path):
|
||||
if os.path.isdir(os.path.join(full_root_path, item)):
|
||||
try:
|
||||
FileHelpers.move_dir(
|
||||
os.path.join(full_root_path, item),
|
||||
os.path.join(new_dir, item),
|
||||
)
|
||||
except Exception as ex:
|
||||
logger.error(f"ERROR IN ZIP IMPORT: {ex}")
|
||||
else:
|
||||
try:
|
||||
FileHelpers.move_file(
|
||||
os.path.join(full_root_path, item),
|
||||
os.path.join(new_dir, item),
|
||||
)
|
||||
except Exception as ex:
|
||||
logger.error(f"ERROR IN ZIP IMPORT: {ex}")
|
||||
except Exception as ex:
|
||||
Console.error(ex)
|
||||
else:
|
||||
return "false"
|
||||
return
|
||||
|
@ -15,6 +15,8 @@ import html
|
||||
import zipfile
|
||||
import pathlib
|
||||
import ctypes
|
||||
import subprocess
|
||||
import itertools
|
||||
from datetime import datetime
|
||||
from socket import gethostname
|
||||
from contextlib import redirect_stderr, suppress
|
||||
@ -22,7 +24,6 @@ from contextlib import redirect_stderr, suppress
|
||||
from app.classes.shared.null_writer import NullWriter
|
||||
from app.classes.shared.console import Console
|
||||
from app.classes.shared.installer import installer
|
||||
from app.classes.shared.file_helpers import FileHelpers
|
||||
from app.classes.shared.translation import Translation
|
||||
from app.classes.web.websocket_helper import WebSocketHelper
|
||||
|
||||
@ -81,6 +82,60 @@ class Helpers:
|
||||
print(f"Import Error: Unable to load {ex.name} module")
|
||||
installer.do_install()
|
||||
|
||||
@staticmethod
|
||||
def find_java_installs():
|
||||
# If we're windows return oracle java versions,
|
||||
# otherwise java vers need to be manual.
|
||||
if os.name == "nt":
|
||||
# Adapted from LeeKamentsky >>>
|
||||
# https://github.com/LeeKamentsky/python-javabridge/blob/master/javabridge/locate.py
|
||||
jdk_key_paths = (
|
||||
"SOFTWARE\\JavaSoft\\JDK",
|
||||
"SOFTWARE\\JavaSoft\\Java Development Kit",
|
||||
)
|
||||
java_paths = []
|
||||
for jdk_key_path in jdk_key_paths:
|
||||
try:
|
||||
with suppress(OSError), winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE, jdk_key_path
|
||||
) as kjdk:
|
||||
for i in itertools.count():
|
||||
version = winreg.EnumKey(kjdk, i)
|
||||
kjdk_current = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
jdk_key_path,
|
||||
)
|
||||
kjdk_current = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
jdk_key_path + "\\" + version,
|
||||
)
|
||||
kjdk_current_values = dict( # pylint: disable=consider-using-dict-comprehension
|
||||
[
|
||||
winreg.EnumValue(kjdk_current, i)[:2]
|
||||
for i in range(winreg.QueryInfoKey(kjdk_current)[1])
|
||||
]
|
||||
)
|
||||
java_paths.append(kjdk_current_values["JavaHome"])
|
||||
except OSError as e:
|
||||
if e.errno == 2:
|
||||
continue
|
||||
raise
|
||||
return java_paths
|
||||
|
||||
# If we get here we're linux so we will use 'update-alternatives'
|
||||
# (If distro does not have update-alternatives then manual input.)
|
||||
try:
|
||||
paths = subprocess.check_output(
|
||||
["/usr/bin/update-alternatives", "--list", "java"], encoding="utf8"
|
||||
)
|
||||
|
||||
if re.match("^(/[^/ ]*)+/?$", paths):
|
||||
return paths.split("\n")
|
||||
|
||||
except Exception as e:
|
||||
print("Java Detect Error: ", e)
|
||||
logger.error(f"Java Detect Error: {e}")
|
||||
|
||||
@staticmethod
|
||||
def float_to_string(gbs: float):
|
||||
s = str(float(gbs) * 1000).rstrip("0").rstrip(".")
|
||||
@ -89,7 +144,8 @@ class Helpers:
|
||||
@staticmethod
|
||||
def check_file_perms(path):
|
||||
try:
|
||||
open(path, "r", encoding="utf-8").close()
|
||||
with open(path, "r", encoding="utf-8"):
|
||||
pass
|
||||
logger.info(f"{path} is readable")
|
||||
return True
|
||||
except PermissionError:
|
||||
@ -425,7 +481,8 @@ class Helpers:
|
||||
def check_writeable(path: str):
|
||||
filename = os.path.join(path, "tempfile.txt")
|
||||
try:
|
||||
open(filename, "w", encoding="utf-8").close()
|
||||
with open(filename, "w", encoding="utf-8"):
|
||||
pass
|
||||
os.remove(filename)
|
||||
|
||||
logger.info(f"{filename} is writable")
|
||||
@ -441,53 +498,6 @@ class Helpers:
|
||||
return ctypes.windll.shell32.IsUserAnAdmin() == 1
|
||||
return os.geteuid() == 0
|
||||
|
||||
@staticmethod
|
||||
def unzip_file(zip_path):
|
||||
new_dir_list = zip_path.split("/")
|
||||
new_dir = ""
|
||||
for i in range(len(new_dir_list) - 1):
|
||||
if i == 0:
|
||||
new_dir += new_dir_list[i]
|
||||
else:
|
||||
new_dir += "/" + new_dir_list[i]
|
||||
|
||||
if Helpers.check_file_perms(zip_path) and os.path.isfile(zip_path):
|
||||
Helpers.ensure_dir_exists(new_dir)
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
try:
|
||||
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
||||
zip_ref.extractall(temp_dir)
|
||||
for i in enumerate(zip_ref.filelist):
|
||||
if len(zip_ref.filelist) > 1 or not zip_ref.filelist[
|
||||
i
|
||||
].filename.endswith("/"):
|
||||
break
|
||||
|
||||
full_root_path = temp_dir
|
||||
|
||||
for item in os.listdir(full_root_path):
|
||||
if os.path.isdir(os.path.join(full_root_path, item)):
|
||||
try:
|
||||
FileHelpers.move_dir(
|
||||
os.path.join(full_root_path, item),
|
||||
os.path.join(new_dir, item),
|
||||
)
|
||||
except Exception as ex:
|
||||
logger.error(f"ERROR IN ZIP IMPORT: {ex}")
|
||||
else:
|
||||
try:
|
||||
FileHelpers.move_file(
|
||||
os.path.join(full_root_path, item),
|
||||
os.path.join(new_dir, item),
|
||||
)
|
||||
except Exception as ex:
|
||||
logger.error(f"ERROR IN ZIP IMPORT: {ex}")
|
||||
except Exception as ex:
|
||||
Console.error(ex)
|
||||
else:
|
||||
return "false"
|
||||
return
|
||||
|
||||
def ensure_logging_setup(self):
|
||||
log_file = os.path.join(os.path.curdir, "logs", "commander.log")
|
||||
session_log_file = os.path.join(os.path.curdir, "logs", "session.log")
|
||||
@ -510,7 +520,8 @@ class Helpers:
|
||||
|
||||
# ensure the log file is there
|
||||
try:
|
||||
open(log_file, "a", encoding="utf-8").close()
|
||||
with open(log_file, "a", encoding="utf-8"):
|
||||
pass
|
||||
except Exception as e:
|
||||
Console.critical(f"Unable to open log file! {e}")
|
||||
sys.exit(1)
|
||||
@ -640,7 +651,7 @@ class Helpers:
|
||||
|
||||
session_data = {"pid": pid, "started": now.strftime("%d-%m-%Y, %H:%M:%S")}
|
||||
with open(self.session_file, "w", encoding="utf-8") as f:
|
||||
json.dump(session_data, f, indent=True)
|
||||
json.dump(session_data, f, indent=4)
|
||||
|
||||
# because this is a recursive function, we will return bytes,
|
||||
# and set human readable later
|
||||
@ -774,13 +785,15 @@ class Helpers:
|
||||
cert.set_version(2)
|
||||
cert.sign(k, "sha256")
|
||||
|
||||
f = open(cert_file, "w", encoding="utf-8")
|
||||
f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode())
|
||||
f.close()
|
||||
with open(cert_file, "w", encoding="utf-8") as cert_file_handle:
|
||||
cert_file_handle.write(
|
||||
crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode()
|
||||
)
|
||||
|
||||
f = open(key_file, "w", encoding="utf-8")
|
||||
f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, k).decode())
|
||||
f.close()
|
||||
with open(key_file, "w", encoding="utf-8") as key_file_handle:
|
||||
key_file_handle.write(
|
||||
crypto.dump_privatekey(crypto.FILETYPE_PEM, k).decode()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def random_string_generator(size=6, chars=string.ascii_uppercase + string.digits):
|
||||
@ -832,7 +845,7 @@ class Helpers:
|
||||
for item in file_list:
|
||||
if os.path.isdir(os.path.join(folder, item)):
|
||||
dir_list.append(item)
|
||||
else:
|
||||
elif str(item) != "crafty.sqlite":
|
||||
unsorted_files.append(item)
|
||||
file_list = sorted(dir_list, key=str.casefold) + sorted(
|
||||
unsorted_files, key=str.casefold
|
||||
@ -863,13 +876,14 @@ class Helpers:
|
||||
|
||||
@staticmethod
|
||||
def generate_dir(folder, output=""):
|
||||
|
||||
dir_list = []
|
||||
unsorted_files = []
|
||||
file_list = os.listdir(folder)
|
||||
for item in file_list:
|
||||
if os.path.isdir(os.path.join(folder, item)):
|
||||
dir_list.append(item)
|
||||
else:
|
||||
elif str(item) != "crafty.sqlite":
|
||||
unsorted_files.append(item)
|
||||
file_list = sorted(dir_list, key=str.casefold) + sorted(
|
||||
unsorted_files, key=str.casefold
|
||||
@ -986,14 +1000,6 @@ class Helpers:
|
||||
[parent_path, child_path]
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def copy_files(source, dest):
|
||||
if os.path.isfile(source):
|
||||
FileHelpers.copy_file(source, dest)
|
||||
logger.info("Copying jar %s to %s", source, dest)
|
||||
else:
|
||||
logger.info("Source jar does not exist.")
|
||||
|
||||
@staticmethod
|
||||
def download_file(executable_url, jar_path):
|
||||
try:
|
||||
@ -1006,7 +1012,8 @@ class Helpers:
|
||||
return False
|
||||
|
||||
try:
|
||||
open(jar_path, "wb").write(response.content)
|
||||
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
|
||||
|
@ -34,8 +34,9 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Controller:
|
||||
def __init__(self, database, helper):
|
||||
def __init__(self, database, helper, file_helper):
|
||||
self.helper: Helpers = helper
|
||||
self.file_helper: FileHelpers = file_helper
|
||||
self.server_jars: ServerJars = ServerJars(helper)
|
||||
self.users_helper: HelperUsers = HelperUsers(database, self.helper)
|
||||
self.roles_helper: HelperRoles = HelperRoles(database)
|
||||
@ -53,7 +54,7 @@ class Controller:
|
||||
)
|
||||
self.server_perms: ServerPermsController = ServerPermsController()
|
||||
self.servers: ServersController = ServersController(
|
||||
self.helper, self.servers_helper, self.management_helper
|
||||
self.helper, self.servers_helper, self.management_helper, self.file_helper
|
||||
)
|
||||
self.users: UsersController = UsersController(
|
||||
self.helper, self.users_helper, self.authentication
|
||||
|
@ -17,8 +17,6 @@ class DatabaseBuilder:
|
||||
logger.info("Fresh Install Detected - Creating Default Settings")
|
||||
Console.info("Fresh Install Detected - Creating Default Settings")
|
||||
default_data = self.helper.find_default_password()
|
||||
# Reset this value if the DB has been dumped
|
||||
self.helper.set_setting("apikey_secret", "random")
|
||||
|
||||
username = default_data.get("username", "admin")
|
||||
password = default_data.get("password", "crafty")
|
||||
|
@ -9,7 +9,6 @@ import threading
|
||||
import logging.config
|
||||
import subprocess
|
||||
import html
|
||||
import tempfile
|
||||
|
||||
# TZLocal is set as a hidden import on win pipeline
|
||||
from tzlocal import get_localzone
|
||||
@ -102,12 +101,14 @@ class ServerOutBuf:
|
||||
class ServerInstance:
|
||||
server_object: Servers
|
||||
helper: Helpers
|
||||
file_helper: FileHelpers
|
||||
management_helper: HelpersManagement
|
||||
stats: Stats
|
||||
stats_helper: HelperServerStats
|
||||
|
||||
def __init__(self, server_id, helper, management_helper, stats):
|
||||
def __init__(self, server_id, helper, management_helper, stats, file_helper):
|
||||
self.helper = helper
|
||||
self.file_helper = file_helper
|
||||
self.management_helper = management_helper
|
||||
# holders for our process
|
||||
self.process = None
|
||||
@ -126,6 +127,7 @@ class ServerInstance:
|
||||
self.stats = stats
|
||||
self.server_object = HelperServers.get_server_obj(self.server_id)
|
||||
self.stats_helper = HelperServerStats(self.server_id)
|
||||
self.last_backup_failed = False
|
||||
try:
|
||||
tz = get_localzone()
|
||||
except ZoneInfoNotFoundError:
|
||||
@ -800,10 +802,9 @@ class ServerInstance:
|
||||
self.server_scheduler.remove_job("c_" + str(self.server_id))
|
||||
|
||||
def agree_eula(self, user_id):
|
||||
file = os.path.join(self.server_path, "eula.txt")
|
||||
f = open(file, "w", encoding="utf-8")
|
||||
f.write("eula=true")
|
||||
f.close()
|
||||
eula_file = os.path.join(self.server_path, "eula.txt")
|
||||
with open(eula_file, "w", encoding="utf-8") as f:
|
||||
f.write("eula=true")
|
||||
self.run_threaded_server(user_id)
|
||||
|
||||
def backup_server(self):
|
||||
@ -846,6 +847,7 @@ class ServerInstance:
|
||||
"backup_reload",
|
||||
{"percent": 0, "total_files": 0},
|
||||
)
|
||||
was_server_running = None
|
||||
logger.info(f"Starting server {self.name} (ID {self.server_id}) backup")
|
||||
server_users = PermissionsServers.get_server_user_list(self.server_id)
|
||||
for user in server_users:
|
||||
@ -858,6 +860,15 @@ class ServerInstance:
|
||||
)
|
||||
time.sleep(3)
|
||||
conf = HelpersManagement.get_backup_config(self.server_id)
|
||||
if conf["shutdown"]:
|
||||
logger.info(
|
||||
"Found shutdown preference. Delaying"
|
||||
+ "backup start. Shutting down server."
|
||||
)
|
||||
if self.check_running():
|
||||
self.stop_server()
|
||||
was_server_running = True
|
||||
|
||||
self.helper.ensure_dir_exists(self.settings["backup_path"])
|
||||
try:
|
||||
backup_filename = (
|
||||
@ -869,62 +880,27 @@ class ServerInstance:
|
||||
f" (ID#{self.server_id}, path={self.server_path}) "
|
||||
f"at '{backup_filename}'"
|
||||
)
|
||||
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
self.server_scheduler.add_job(
|
||||
self.backup_status,
|
||||
"interval",
|
||||
seconds=1,
|
||||
id="backup_" + str(self.server_id),
|
||||
args=[temp_dir + "/", backup_filename + ".zip"],
|
||||
)
|
||||
# pylint: disable=unexpected-keyword-arg
|
||||
try:
|
||||
FileHelpers.copy_dir(self.server_path, temp_dir, dirs_exist_ok=True)
|
||||
except shutil.Error as e:
|
||||
logger.error(f"Failed to fully complete backup due to shutil error {e}")
|
||||
excluded_dirs = HelpersManagement.get_excluded_backup_dirs(self.server_id)
|
||||
server_dir = Helpers.get_os_understandable_path(self.settings["path"])
|
||||
|
||||
for my_dir in excluded_dirs:
|
||||
# Take the full path of the excluded dir and replace the
|
||||
# server path with the temp path, this is so that we're
|
||||
# only deleting excluded dirs from the temp path
|
||||
# and not the server path
|
||||
excluded_dir = Helpers.get_os_understandable_path(my_dir).replace(
|
||||
server_dir, Helpers.get_os_understandable_path(temp_dir)
|
||||
)
|
||||
# Next, check to see if it is a directory
|
||||
if os.path.isdir(excluded_dir):
|
||||
# If it is a directory,
|
||||
# recursively delete the entire directory from the backup
|
||||
try:
|
||||
FileHelpers.del_dirs(excluded_dir)
|
||||
except FileNotFoundError:
|
||||
Console.error(
|
||||
f"Excluded dir {excluded_dir} not found. Moving on..."
|
||||
)
|
||||
else:
|
||||
# If not, just remove the file
|
||||
try:
|
||||
os.remove(excluded_dir)
|
||||
except:
|
||||
Console.error(
|
||||
f"Excluded dir {excluded_dir} not found. Moving on..."
|
||||
)
|
||||
if conf["compress"]:
|
||||
logger.debug(
|
||||
"Found compress backup to be true. Calling compressed archive"
|
||||
)
|
||||
FileHelpers.make_compressed_archive(
|
||||
Helpers.get_os_understandable_path(backup_filename), temp_dir
|
||||
self.file_helper.make_compressed_backup(
|
||||
Helpers.get_os_understandable_path(backup_filename),
|
||||
server_dir,
|
||||
excluded_dirs,
|
||||
self.server_id,
|
||||
)
|
||||
else:
|
||||
logger.debug(
|
||||
"Found compress backup to be false. Calling NON-compressed archive"
|
||||
)
|
||||
FileHelpers.make_archive(
|
||||
Helpers.get_os_understandable_path(backup_filename), temp_dir
|
||||
self.file_helper.make_backup(
|
||||
Helpers.get_os_understandable_path(backup_filename),
|
||||
server_dir,
|
||||
excluded_dirs,
|
||||
self.server_id,
|
||||
)
|
||||
|
||||
while (
|
||||
@ -939,7 +915,6 @@ class ServerInstance:
|
||||
|
||||
self.is_backingup = False
|
||||
logger.info(f"Backup of server: {self.name} completed")
|
||||
self.server_scheduler.remove_job("backup_" + str(self.server_id))
|
||||
results = {"percent": 100, "total_files": 0, "current_file": 0}
|
||||
if len(self.helper.websocket_helper.clients) > 0:
|
||||
self.helper.websocket_helper.broadcast_page_params(
|
||||
@ -959,12 +934,17 @@ class ServerInstance:
|
||||
HelperUsers.get_user_lang_by_id(user),
|
||||
).format(self.name),
|
||||
)
|
||||
if was_server_running:
|
||||
logger.info(
|
||||
"Backup complete. User had shutdown preference. Starting server."
|
||||
)
|
||||
self.start_server(HelperUsers.get_user_id_by_name("system"))
|
||||
time.sleep(3)
|
||||
self.last_backup_failed = False
|
||||
except:
|
||||
logger.exception(
|
||||
f"Failed to create backup of server {self.name} (ID {self.server_id})"
|
||||
)
|
||||
self.server_scheduler.remove_job("backup_" + str(self.server_id))
|
||||
results = {"percent": 100, "total_files": 0, "current_file": 0}
|
||||
if len(self.helper.websocket_helper.clients) > 0:
|
||||
self.helper.websocket_helper.broadcast_page_params(
|
||||
@ -974,8 +954,12 @@ class ServerInstance:
|
||||
results,
|
||||
)
|
||||
self.is_backingup = False
|
||||
finally:
|
||||
FileHelpers.del_dirs(temp_dir)
|
||||
if was_server_running:
|
||||
logger.info(
|
||||
"Backup complete. User had shutdown preference. Starting server."
|
||||
)
|
||||
self.start_server(HelperUsers.get_user_id_by_name("system"))
|
||||
self.last_backup_failed = True
|
||||
|
||||
def backup_status(self, source_path, dest_path):
|
||||
results = Helpers.calc_percent(source_path, dest_path)
|
||||
@ -988,6 +972,9 @@ class ServerInstance:
|
||||
results,
|
||||
)
|
||||
|
||||
def last_backup_status(self):
|
||||
return self.last_backup_failed
|
||||
|
||||
def send_backup_status(self):
|
||||
try:
|
||||
return self.backup_stats
|
||||
@ -1093,7 +1080,7 @@ class ServerInstance:
|
||||
)
|
||||
|
||||
# copies to backup dir
|
||||
Helpers.copy_files(current_executable, backup_executable)
|
||||
FileHelpers.copy_file(current_executable, backup_executable)
|
||||
|
||||
# boolean returns true for false for success
|
||||
downloaded = Helpers.download_file(
|
||||
|
@ -220,7 +220,7 @@ class FileHandler(BaseHandler):
|
||||
path = Helpers.get_os_understandable_path(self.get_argument("path", None))
|
||||
if Helpers.is_os_windows():
|
||||
path = Helpers.wtol_path(path)
|
||||
Helpers.unzip_file(path)
|
||||
FileHelpers.unzip_file(path)
|
||||
self.redirect(f"/panel/server_detail?id={server_id}&subpage=files")
|
||||
return
|
||||
|
||||
|
@ -6,6 +6,7 @@ import typing as t
|
||||
import json
|
||||
import logging
|
||||
import threading
|
||||
import shlex
|
||||
import bleach
|
||||
import libgravatar
|
||||
import requests
|
||||
@ -482,6 +483,14 @@ class PanelHandler(BaseHandler):
|
||||
if str(server_id) not in server_ids[:]:
|
||||
user_order.remove(server_id)
|
||||
page_data["servers"] = page_servers
|
||||
for server in page_data["servers"]:
|
||||
server_obj = self.controller.servers.get_server_instance_by_id(
|
||||
server["server_data"]["server_id"]
|
||||
)
|
||||
alert = False
|
||||
if server_obj.last_backup_status():
|
||||
alert = True
|
||||
server["alert"] = alert
|
||||
|
||||
# num players is set to zero here. If we poll all servers while
|
||||
# dashboard is loading it takes FOREVER. We leave this to the
|
||||
@ -497,6 +506,10 @@ class PanelHandler(BaseHandler):
|
||||
if server_id is None:
|
||||
return
|
||||
|
||||
server_obj = self.controller.servers.get_server_instance_by_id(server_id)
|
||||
page_data["backup_failed"] = server_obj.last_backup_status()
|
||||
server_obj = None
|
||||
|
||||
valid_subpages = [
|
||||
"term",
|
||||
"logs",
|
||||
@ -627,6 +640,18 @@ class PanelHandler(BaseHandler):
|
||||
"/panel/error?error=Unauthorized access Server Config"
|
||||
)
|
||||
return
|
||||
page_data["java_versions"] = Helpers.find_java_installs()
|
||||
server_obj: Servers = self.controller.servers.get_server_obj(server_id)
|
||||
page_java = []
|
||||
page_data["java_versions"].append("java")
|
||||
for version in page_data["java_versions"]:
|
||||
if os.name == "nt":
|
||||
page_java.append(version)
|
||||
else:
|
||||
if len(version) > 0:
|
||||
page_java.append(version)
|
||||
|
||||
page_data["java_versions"] = page_java
|
||||
|
||||
if subpage == "files":
|
||||
if (
|
||||
@ -1342,6 +1367,8 @@ class PanelHandler(BaseHandler):
|
||||
if Helpers.is_os_windows():
|
||||
log_path.replace(" ", "^ ")
|
||||
log_path = Helpers.wtol_path(log_path)
|
||||
if not self.helper.validate_traversal(server_obj.path, log_path):
|
||||
log_path = ""
|
||||
executable = self.get_argument("executable", None)
|
||||
execution_command = self.get_argument("execution_command", None)
|
||||
server_ip = self.get_argument("server_ip", None)
|
||||
@ -1355,11 +1382,50 @@ class PanelHandler(BaseHandler):
|
||||
auto_start = int(float(self.get_argument("auto_start", "0")))
|
||||
crash_detection = int(float(self.get_argument("crash_detection", "0")))
|
||||
logs_delete_after = int(float(self.get_argument("logs_delete_after", "0")))
|
||||
java_selection = self.get_argument("java_selection", None)
|
||||
# subpage = self.get_argument('subpage', None)
|
||||
|
||||
server_id = self.check_server_id()
|
||||
if server_id is None:
|
||||
return
|
||||
if java_selection:
|
||||
try:
|
||||
execution_list = shlex.split(execution_command)
|
||||
except ValueError:
|
||||
self.redirect(
|
||||
"/panel/error?error=Invalid execution command. Java path"
|
||||
" must be surrounded by quotes."
|
||||
" (Are you missing a closing quote?)"
|
||||
)
|
||||
if (
|
||||
not any(
|
||||
java_selection in path for path in Helpers.find_java_installs()
|
||||
)
|
||||
and java_selection != "java"
|
||||
):
|
||||
self.redirect(
|
||||
"/panel/error?error=Attack attempted."
|
||||
+ " A copy of this report is being sent to server owner."
|
||||
)
|
||||
self.controller.management.add_to_audit_log_raw(
|
||||
exec_user["username"],
|
||||
exec_user["user_id"],
|
||||
server_id,
|
||||
f"Attempted to send bad java path for {server_id}."
|
||||
+ " Possible attack. Act accordingly.",
|
||||
self.get_remote_ip(),
|
||||
)
|
||||
return
|
||||
if java_selection != "java":
|
||||
if self.helper.is_os_windows():
|
||||
execution_list[0] = '"' + java_selection + '/bin/java"'
|
||||
else:
|
||||
execution_list[0] = '"' + java_selection + '"'
|
||||
else:
|
||||
execution_list[0] = "java"
|
||||
execution_command = ""
|
||||
for item in execution_list:
|
||||
execution_command += item + " "
|
||||
|
||||
server_obj: Servers = self.controller.servers.get_server_obj(server_id)
|
||||
stale_executable = server_obj.executable
|
||||
@ -1389,7 +1455,7 @@ class PanelHandler(BaseHandler):
|
||||
server_obj.path = server_obj.path
|
||||
server_obj.log_path = server_obj.log_path
|
||||
server_obj.executable = server_obj.executable
|
||||
server_obj.execution_command = server_obj.execution_command
|
||||
server_obj.execution_command = execution_command
|
||||
server_obj.server_ip = server_obj.server_ip
|
||||
server_obj.server_port = server_obj.server_port
|
||||
server_obj.executable_update_url = server_obj.executable_update_url
|
||||
@ -1433,6 +1499,7 @@ class PanelHandler(BaseHandler):
|
||||
|
||||
server_obj = self.controller.servers.get_server_obj(server_id)
|
||||
compress = self.get_argument("compress", False)
|
||||
shutdown = self.get_argument("shutdown", False)
|
||||
check_changed = self.get_argument("changed")
|
||||
if str(check_changed) == str(1):
|
||||
checked = self.get_body_arguments("root_path")
|
||||
@ -1448,6 +1515,18 @@ class PanelHandler(BaseHandler):
|
||||
max_backups = bleach.clean(self.get_argument("max_backups", None))
|
||||
|
||||
server_obj = self.controller.servers.get_server_obj(server_id)
|
||||
if (
|
||||
not backup_path
|
||||
== self.helper.wtol_path(
|
||||
os.path.join(self.helper.backup_path, server_obj.server_uuid)
|
||||
)
|
||||
and self.helper.wtol_path(self.controller.project_root) in backup_path
|
||||
):
|
||||
self.redirect(
|
||||
"/panel/error?error=Nefarious activities detected."
|
||||
" User attempted to make backup path within Crafty's root."
|
||||
)
|
||||
return
|
||||
server_obj.backup_path = backup_path
|
||||
self.controller.servers.update_server(server_obj)
|
||||
self.controller.management.set_backup_config(
|
||||
@ -1455,6 +1534,7 @@ class PanelHandler(BaseHandler):
|
||||
max_backups=max_backups,
|
||||
excluded_dirs=checked,
|
||||
compress=bool(compress),
|
||||
shutdown=bool(shutdown),
|
||||
)
|
||||
|
||||
self.controller.management.add_to_audit_log(
|
||||
@ -1941,7 +2021,10 @@ class PanelHandler(BaseHandler):
|
||||
self.redirect("/panel/error?error=Invalid Key ID")
|
||||
return
|
||||
|
||||
if key.user_id != exec_user["user_id"]:
|
||||
if (
|
||||
str(key.user_id) != str(exec_user["user_id"])
|
||||
and not exec_user["superuser"]
|
||||
):
|
||||
self.redirect(
|
||||
"/panel/error?error=You are not authorized to access this key."
|
||||
)
|
||||
|
@ -17,6 +17,15 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ServerHandler(BaseHandler):
|
||||
def get_user_roles(self):
|
||||
user_roles = {}
|
||||
for user_id in self.controller.users.get_all_user_ids():
|
||||
user_roles_list = self.controller.users.get_user_roles_names(user_id)
|
||||
# user_servers =
|
||||
# self.controller.servers.get_authorized_servers(user.user_id)
|
||||
user_roles[user_id] = user_roles_list
|
||||
return user_roles
|
||||
|
||||
@tornado.web.authenticated
|
||||
def get(self, page):
|
||||
(
|
||||
@ -271,11 +280,19 @@ class ServerHandler(BaseHandler):
|
||||
)
|
||||
|
||||
if page == "step1":
|
||||
if not superuser and not self.controller.crafty_perms.can_create_server(
|
||||
exec_user["user_id"]
|
||||
):
|
||||
self.redirect(
|
||||
"/panel/error?error=Unauthorized access: "
|
||||
"not a server creator or server limit reached"
|
||||
)
|
||||
return
|
||||
|
||||
if not superuser:
|
||||
user_roles = self.controller.roles.get_all_roles()
|
||||
else:
|
||||
user_roles = self.controller.roles.get_all_roles()
|
||||
user_roles = self.get_user_roles()
|
||||
server = bleach.clean(self.get_argument("server", ""))
|
||||
server_name = bleach.clean(self.get_argument("server_name", ""))
|
||||
min_mem = bleach.clean(self.get_argument("min_memory", ""))
|
||||
@ -396,6 +413,14 @@ class ServerHandler(BaseHandler):
|
||||
self.redirect("/panel/dashboard")
|
||||
|
||||
if page == "bedrock_step1":
|
||||
if not superuser and not self.controller.crafty_perms.can_create_server(
|
||||
exec_user["user_id"]
|
||||
):
|
||||
self.redirect(
|
||||
"/panel/error?error=Unauthorized access: "
|
||||
"not a server creator or server limit reached"
|
||||
)
|
||||
return
|
||||
if not superuser:
|
||||
user_roles = self.controller.roles.get_all_roles()
|
||||
else:
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"major": 4,
|
||||
"minor": 0,
|
||||
"sub": 3,
|
||||
"sub": 4,
|
||||
"meta": "beta"
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
}
|
||||
|
||||
|
||||
.sidebar > .nav > .nav-item:not(.nav-profile) > .nav-link:before {
|
||||
.sidebar>.nav>.nav-item:not(.nav-profile)>.nav-link:before {
|
||||
content: none;
|
||||
position: absolute;
|
||||
left: 30px;
|
||||
@ -21,7 +21,7 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sidebar > .nav > .nav-item:not(.nav-profile) > .nav-link:before {
|
||||
.sidebar>.nav>.nav-item:not(.nav-profile)>.nav-link:before {
|
||||
content: none;
|
||||
position: absolute;
|
||||
left: 30px;
|
||||
@ -33,43 +33,48 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sidebar > .nav .nav-item .nav-link, .collapsed{
|
||||
.sidebar>.nav .nav-item .nav-link,
|
||||
.collapsed {
|
||||
padding: 15px 30px;
|
||||
}
|
||||
|
||||
.mc-log-time{
|
||||
color:#19d895;
|
||||
.mc-log-time {
|
||||
color: #19d895;
|
||||
}
|
||||
|
||||
.mc-log-info{
|
||||
color:#8862e0;
|
||||
.mc-log-info {
|
||||
color: #8862e0;
|
||||
}
|
||||
|
||||
.mc-log-warn{
|
||||
color:#ffaf00;
|
||||
.mc-log-warn {
|
||||
color: #ffaf00;
|
||||
}
|
||||
|
||||
.mc-log-error{
|
||||
color:#af463f;
|
||||
.mc-log-error {
|
||||
color: #af463f;
|
||||
}
|
||||
|
||||
.mc-log-fatal{
|
||||
color:#da0f00;
|
||||
.mc-log-fatal {
|
||||
color: #da0f00;
|
||||
}
|
||||
|
||||
.mc-log-keyword{
|
||||
color:#2196f3;
|
||||
.mc-log-keyword {
|
||||
color: #2196f3;
|
||||
}
|
||||
|
||||
.scrollable-element {
|
||||
scrollbar-color: red yellow;
|
||||
scrollbar-color: red yellow;
|
||||
}
|
||||
|
||||
.term-nav-item {
|
||||
padding: 1%;
|
||||
}
|
||||
|
||||
/* Fix body scrollbar color */
|
||||
body { background-color: var(--dark) !important; /* Firefox */ }
|
||||
body {
|
||||
background-color: var(--dark) !important;
|
||||
/* Firefox */
|
||||
}
|
||||
|
||||
/* Webkit */
|
||||
/* Didn't really work out
|
||||
@ -81,11 +86,20 @@ body { background-color: var(--dark) !important; /* Firefox */ }
|
||||
::-webkit-scrollbar-track { background-color: #202538; }
|
||||
::-webkit-scrollbar-corner { background-color: #202538; }*/
|
||||
|
||||
.actions_serverlist > a > i {
|
||||
cursor: pointer;
|
||||
.actions_serverlist>a>i {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.actions_serveritem {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.corner {
|
||||
position: absolute;
|
||||
margin-top: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.accordion .card {
|
||||
margin-bottom: 0px;
|
||||
}
|
@ -25,21 +25,21 @@
|
||||
<!-- Page Title Header Ends-->
|
||||
{% if data['first_log'] %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$(document).ready(function () {
|
||||
bootbox.alert({
|
||||
backdrop: true,
|
||||
title: 'Your Feedback Is Appreciated...',
|
||||
message: '<p>We will only request this information from the admin user once... 😊</p>'
|
||||
+'<p><strong>All data collected is completely anonymous</strong> and is only used to improve Crafty 4.0 and allow us to more accurately report the number of Crafty 4 users.</p><iframe width="640px" height="480px"'
|
||||
+'src="https://forms.office.com/Pages/ResponsePage.aspx?id=LwLajNkpXU2CKc95G1oO3MN0Hu3oEUNLr-EtLx31TS5UNUNVQlFNVUVYMEc'
|
||||
+'1V1BKS0FQUUlERUtWQy4u&embed=true" frameborder="0" marginwidth="0" marginheight="0" style="border: none; max-width:100%;'
|
||||
+' max-height:100vh" allowfullscreen webkitallowfullscreen mozallowfullscreen msallowfullscreen> </iframe>',
|
||||
buttons: {
|
||||
ok: {
|
||||
backdrop: true,
|
||||
title: 'Your Feedback Is Appreciated...',
|
||||
message: '<p>We will only request this information from the admin user once... 😊</p>'
|
||||
+ '<p><strong>All data collected is completely anonymous</strong> and is only used to improve Crafty 4.0 and allow us to more accurately report the number of Crafty 4 users.</p><iframe width="640px" height="480px"'
|
||||
+ 'src="https://forms.office.com/Pages/ResponsePage.aspx?id=LwLajNkpXU2CKc95G1oO3MN0Hu3oEUNLr-EtLx31TS5UNUNVQlFNVUVYMEc'
|
||||
+ '1V1BKS0FQUUlERUtWQy4u&embed=true" frameborder="0" marginwidth="0" marginheight="0" style="border: none; max-width:100%;'
|
||||
+ ' max-height:100vh" allowfullscreen webkitallowfullscreen mozallowfullscreen msallowfullscreen> </iframe>',
|
||||
buttons: {
|
||||
ok: {
|
||||
label: 'Skip Survey/Done',
|
||||
className: 'btn-secondary'
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@ -125,19 +125,19 @@
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="table-responsive">
|
||||
{% if len(data['servers']) == 0%}
|
||||
<div style="text-align: center; color: grey;">
|
||||
<h1>{{ translate('dashboard', 'welcome', data['lang']) }}</h1>
|
||||
<br>
|
||||
<h7>{{ translate('dashboard', 'no-servers', data['lang']) }} {{ translate('dashboard', 'newServer',
|
||||
data['lang']) }}.</h7>
|
||||
</div>
|
||||
{% if len(data['servers']) == 0%}
|
||||
<div style="text-align: center; color: grey;">
|
||||
<h1>{{ translate('dashboard', 'welcome', data['lang']) }}</h1>
|
||||
<br>
|
||||
<h7>{{ translate('dashboard', 'no-servers', data['lang']) }} {{ translate('dashboard', 'newServer',
|
||||
data['lang']) }}.</h7>
|
||||
</div>
|
||||
|
||||
{% end %}
|
||||
{% if len(data['servers']) > 0 %}
|
||||
<!-- View for Large screen -->
|
||||
<table id="servers_table" class="table table-hover d-none d-sm-table">
|
||||
{% end %}
|
||||
{% if len(data['servers']) > 0 %}
|
||||
<!-- View for Large screen -->
|
||||
<div class="table-responsive d-none d-sm-block">
|
||||
<table id="servers_table" class="table table-hover">
|
||||
<thead>
|
||||
<tr class="rounded" id="first" draggable="false">
|
||||
<th draggable="false">{{ translate('dashboard', 'server', data['lang']) }}</th>
|
||||
@ -154,9 +154,16 @@
|
||||
<tr id="{{server['server_data']['server_id']}}" draggable="true">
|
||||
<td draggable="false">
|
||||
<i class="fas fa-server"></i>
|
||||
{% if server['alert'] %}
|
||||
<a style="color: red !important;" draggable="false"
|
||||
href="/panel/server_detail?id={{server['server_data']['server_id']}}">
|
||||
{{ server['server_data']['server_name'] }} <i class="fas fa-exclamation-triangle"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
<a draggable="false" href="/panel/server_detail?id={{server['server_data']['server_id']}}">
|
||||
{{ server['server_data']['server_name'] }}
|
||||
</a>
|
||||
{% end %}
|
||||
</td>
|
||||
|
||||
<td draggable="false" id="controls{{server['server_data']['server_id']}}" class="actions_serverlist">
|
||||
@ -285,175 +292,224 @@
|
||||
{% end %}
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- View for Small screen -->
|
||||
<table id="servers_table" class="table table-hover d-table d-sm-none">
|
||||
<thead>
|
||||
<tr class="rounded" id="first" draggable="false">
|
||||
<th scope="col" draggable="false">{{ translate('dashboard', 'server', data['lang']) }}</th>
|
||||
<th scope="col" draggable="false">{{ translate('dashboard', 'actions', data['lang']) }}</th>
|
||||
<th scope="col" draggable="false">{{ translate('dashboard', 'status', data['lang']) }}</th>
|
||||
<th scope="col" draggable="false"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for server in data['servers'] %}
|
||||
<tr id="{{server['server_data']['server_id']}}" draggable="false">
|
||||
<td scope="row"><i class="fas fa-server"></i>
|
||||
<a draggable="false" href="/panel/server_detail?id={{server['server_data']['server_id']}}">
|
||||
{{ server['server_data']['server_name'] }}
|
||||
</a>
|
||||
</td>
|
||||
<td draggable="false" id="controls{{server['server_data']['server_id']}}" class="actions_serverlist">
|
||||
{% if server['user_command_permission'] %}
|
||||
{% if server['stats']['running'] %}
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="stop_button" data-toggle="tooltip"
|
||||
title="{{ translate('dashboard', 'stop' , data['lang']) }}">
|
||||
<i class="fas fa-stop"></i>
|
||||
</a>
|
||||
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="restart_button" data-toggle="tooltip"
|
||||
title="{{ translate('dashboard', 'restart' , data['lang']) }}">
|
||||
<i class="fas fa-sync"></i>
|
||||
</a>
|
||||
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="kill_button" data-toggle="tooltip"
|
||||
title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
||||
<i class="fas fa-skull"></i>
|
||||
</a>
|
||||
|
||||
{% elif server['stats']['updating']%}
|
||||
<!-- WHAT HAPPENED HERE -->
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="">{{ translate('serverTerm', 'updating',
|
||||
data['lang']) }}</i></a>
|
||||
{% elif server['stats']['waiting_start']%}
|
||||
<!-- WHAT HAPPENED HERE -->
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="" title="{{
|
||||
translate('dashboard', 'delay-explained' , data['lang'])}}">{{ translate('dashboard', 'starting',
|
||||
data['lang']) }}</i></a>
|
||||
{% elif server['stats']['downloading']%}
|
||||
<a data-id="{{server['server_data']['server_id']}}" class=""><i class="fa fa-spinner fa-spin"></i>
|
||||
{{ translate('serverTerm', 'downloading', data['lang']) }}</a>
|
||||
{% else %}
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="play_button" data-toggle="tooltip"
|
||||
title="{{ translate('dashboard', 'start' , data['lang']) }}">
|
||||
<i class="fas fa-play"></i>
|
||||
</a>
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="clone_button" data-toggle="tooltip"
|
||||
title="{{ translate('dashboard', 'clone' , data['lang']) }}">
|
||||
<i class="fas fa-clone"></i>
|
||||
</a>
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="kill_button" data-toggle="tooltip"
|
||||
title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
||||
<i class="fas fa-skull"></i>
|
||||
</a>
|
||||
{% end %}
|
||||
{% end %}
|
||||
</td>
|
||||
<td draggable="false" id="m_server_running_status_{{server['server_data']['server_id']}}">
|
||||
{% if server['stats']['running'] %}
|
||||
<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online',
|
||||
data['lang']) }}</span>
|
||||
{% elif server['stats']['crashed'] %}
|
||||
<span class="text-danger"><i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard',
|
||||
'crashed',
|
||||
data['lang']) }}</span>
|
||||
{% else %}
|
||||
<span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline',
|
||||
data['lang']) }}</span>
|
||||
{% end %}
|
||||
</td>
|
||||
<td>
|
||||
<span data-toggle="collapse" data-target="#details_{{server['server_data']['server_id']}}"
|
||||
aria-expanded="false" aria-controls="details_{{server['server_data']['server_id']}}"><i
|
||||
class="fas fa-chevron-down"></i></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="details_{{server['server_data']['server_id']}}" class="collapse" draggable="false">
|
||||
<td colspan="4">
|
||||
<div class="collapse" id="details_{{server['server_data']['server_id']}}">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h6>{{ translate('dashboard', 'cpuUsage', data['lang']) }}</h6>
|
||||
<div id="m_server_cpu_{{server['server_data']['server_id']}}">
|
||||
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
||||
title="{{server['stats']['cpu']}}">
|
||||
<div class="progress-bar
|
||||
{% if server['stats']['cpu'] <= 33 %}
|
||||
bg-success
|
||||
{% elif 34 <= server['stats']['cpu'] <= 66 %}
|
||||
bg-warning
|
||||
{% else %}
|
||||
bg-danger
|
||||
{% end %}
|
||||
" role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0"
|
||||
aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
{{server['stats']['cpu']}}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<h6>{{ translate('dashboard', 'memUsage', data['lang']) }}</h6>
|
||||
<div draggable="false" id="m_server_mem_{{server['server_data']['server_id']}}">
|
||||
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
||||
title="{{server['stats']['mem']}}">
|
||||
<div class="progress-bar
|
||||
{% if server['stats']['mem_percent'] <= 33 %}
|
||||
bg-success
|
||||
{% elif 34 <= server['stats']['mem_percent'] <= 66 %}
|
||||
bg-warning
|
||||
{% else %}
|
||||
bg-danger
|
||||
{% end %}
|
||||
" role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0"
|
||||
aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
{{server['stats']['mem_percent']}}% -
|
||||
|
||||
{% if server['stats']['mem'] == 0 %}
|
||||
0 MB
|
||||
{% else %}
|
||||
{{server['stats']['mem']}}
|
||||
{% end %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% end %}
|
||||
{% if len(data['servers']) > 0 %}
|
||||
<!-- Try with Accordion -->
|
||||
<div class="d-sm-none d-block">
|
||||
<div class="accordion" id="accordionServers">
|
||||
{% for server in data['servers'] %}
|
||||
<div class="card">
|
||||
<div class="card-header" id="heading-{{server['server_data']['server_id']}}">
|
||||
<h2 class="mb-0 container overflow-hidden">
|
||||
<div class="row">
|
||||
<div class="col-10 col-lg-3 mx-0 px-0">
|
||||
{% if server['alert'] %}
|
||||
<a style="color: red !important" class="btn btn-link d-flex justify-content-start" type="button"
|
||||
href="/panel/server_detail?id={{server['server_data']['server_id']}}">
|
||||
<i class="fas fa-server"></i> {{ server['server_data']['server_name'] }} <i
|
||||
class="fas fa-exclamation-triangle"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
<a class="btn btn-link d-flex justify-content-start" type="button"
|
||||
href="/panel/server_detail?id={{server['server_data']['server_id']}}">
|
||||
<i class="fas fa-server"></i> {{ server['server_data']['server_name'] }}
|
||||
</a>
|
||||
{% end %}
|
||||
</div>
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h6>{{ translate('dashboard', 'size', data['lang']) }}</h6>
|
||||
<div draggable="false" id="m_server_world_{{server['server_data']['server_id']}}">
|
||||
{{ server['stats']['world_size'] }}
|
||||
<div class="col-2 col-lg-3 mx-0 px-0">
|
||||
<a class="btn btn-link d-flex justify-content-center" type="button" data-toggle="collapse"
|
||||
data-target="#collapse-{{server['server_data']['server_id']}}" aria-expanded="false"
|
||||
aria-controls="collapse-{{server['server_data']['server_id']}}">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-4 col-lg-3 mx-0 px-0">
|
||||
<a id="m_server_running_status_{{server['server_data']['server_id']}}"
|
||||
class="btn btn-link d-flex justify-content-start" type="button">
|
||||
{% if server['stats']['running'] %}
|
||||
<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online',
|
||||
data['lang']) }}</span>
|
||||
{% elif server['stats']['crashed'] %}
|
||||
<span class="text-danger"><i class="fas fa-exclamation-triangle"></i> {{
|
||||
translate('dashboard',
|
||||
'crashed',
|
||||
data['lang']) }}</span>
|
||||
{% else %}
|
||||
<span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline',
|
||||
data['lang']) }}</span>
|
||||
{% end %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-8 col-lg-3 mx-0 px-0">
|
||||
<div id="controls{{server['server_data']['server_id']}}" class="container overflow-hidden">
|
||||
{% if server['user_command_permission'] %}
|
||||
{% if server['stats']['running'] %}
|
||||
<div class="row">
|
||||
<div class="col-4 px-0">
|
||||
<a data-id="{{server['server_data']['server_id']}}"
|
||||
class="btn btn-link stop_button actions_serveritem" data-toggle="tooltip"
|
||||
title="{{ translate('dashboard', 'stop' , data['lang']) }}">
|
||||
<i class="fas fa-stop"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-4 px-0">
|
||||
<a data-id="{{server['server_data']['server_id']}}"
|
||||
class="btn btn-link restart_button actions_serveritem" data-toggle="tooltip"
|
||||
title="{{ translate('dashboard', 'restart' , data['lang']) }}">
|
||||
<i class="fas fa-sync"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-4 px-0">
|
||||
<a data-id="{{server['server_data']['server_id']}}"
|
||||
class="btn btn-link kill_button actions_serveritem" data-toggle="tooltip"
|
||||
title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
||||
<i class="fas fa-skull"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6" style="width: auto;">
|
||||
<h6>{{ translate('dashboard', 'players', data['lang']) }}</h6>
|
||||
<div draggable="false" id="m_server_desc_{{server['server_data']['server_id']}}">
|
||||
{% if server['stats']['int_ping_results'] %}
|
||||
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard',
|
||||
'max',
|
||||
data['lang']) }} <br />
|
||||
|
||||
{% if server['stats']['desc'] != 'False' %}
|
||||
<div id="desc_id"
|
||||
style="overflow-wrap: break-word !important; max-width: 85px !important; overflow: scroll;">
|
||||
{{ server['stats']['desc'] }}</div> <br />
|
||||
{% end %}
|
||||
|
||||
{% if server['stats']['version'] != 'False' %}
|
||||
{{ server['stats']['version'] }}
|
||||
{% end %}
|
||||
{% end %}
|
||||
{% elif server['stats']['updating']%}
|
||||
<!-- WHAT HAPPENED HERE -->
|
||||
<div class="row">
|
||||
<div class="col-12 px-0">
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="btn btn-link">{{
|
||||
translate('serverTerm', 'updating',
|
||||
data['lang']) }}</i></a>
|
||||
</div>
|
||||
</div>
|
||||
{% elif server['stats']['waiting_start']%}
|
||||
<!-- WHAT HAPPENED HERE -->
|
||||
<div class="row">
|
||||
<div class="col-12 px-0">
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="btn btn-link" title="{{
|
||||
translate('dashboard', 'delay-explained' , data['lang'])}}">{{ translate('dashboard', 'starting',
|
||||
data['lang']) }}</i></a>
|
||||
</div>
|
||||
</div>
|
||||
{% elif server['stats']['downloading']%}
|
||||
<div class="row">
|
||||
<div class="col-12 px-0">
|
||||
<a data-id="{{server['server_data']['server_id']}}" class="btn btn-link"><i
|
||||
class="fa fa-spinner fa-spin"></i>
|
||||
{{ translate('serverTerm', 'downloading', data['lang']) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="row">
|
||||
<div class="col-4 px-0">
|
||||
<a data-id="{{server['server_data']['server_id']}}"
|
||||
class="btn play_button actions_serveritem" data-toggle="tooltip"
|
||||
title="{{ translate('dashboard', 'start' , data['lang']) }}">
|
||||
<i class="fas fa-play"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-4 px-0">
|
||||
<a data-id="{{server['server_data']['server_id']}}"
|
||||
class="btn clone_button actions_serveritem" data-toggle="tooltip"
|
||||
title="{{ translate('dashboard', 'clone' , data['lang']) }}">
|
||||
<i class="fas fa-clone"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-4 px-0">
|
||||
<a data-id="{{server['server_data']['server_id']}}"
|
||||
class="btn kill_button actions_serveritem" data-toggle="tooltip"
|
||||
title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
||||
<i class="fas fa-skull"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
{% end %}
|
||||
{% end %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% end %}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div id="collapse-{{server['server_data']['server_id']}}" class="collapse"
|
||||
aria-labelledby="heading-{{server['server_data']['server_id']}}" data-parent="#accordionServers">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h6>{{ translate('dashboard', 'cpuUsage', data['lang']) }}</h6>
|
||||
<div id="m_server_cpu_{{server['server_data']['server_id']}}">
|
||||
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
||||
title="{{server['stats']['cpu']}}">
|
||||
<div class="progress-bar
|
||||
{% if server['stats']['cpu'] <= 33 %}
|
||||
bg-success
|
||||
{% elif 34 <= server['stats']['cpu'] <= 66 %}
|
||||
bg-warning
|
||||
{% else %}
|
||||
bg-danger
|
||||
{% end %}
|
||||
" role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0" aria-valuemin="0"
|
||||
aria-valuemax="100"></div>
|
||||
</div>
|
||||
{{server['stats']['cpu']}}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<h6>{{ translate('dashboard', 'memUsage', data['lang']) }}</h6>
|
||||
<div draggable="false" id="m_server_mem_{{server['server_data']['server_id']}}">
|
||||
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
||||
title="{{server['stats']['mem']}}">
|
||||
<div class="progress-bar
|
||||
{% if server['stats']['mem_percent'] <= 33 %}
|
||||
bg-success
|
||||
{% elif 34 <= server['stats']['mem_percent'] <= 66 %}
|
||||
bg-warning
|
||||
{% else %}
|
||||
bg-danger
|
||||
{% end %}
|
||||
" role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0"
|
||||
aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
{{server['stats']['mem_percent']}}% -
|
||||
|
||||
{% if server['stats']['mem'] == 0 %}
|
||||
0 MB
|
||||
{% else %}
|
||||
{{server['stats']['mem']}}
|
||||
{% end %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h6>{{ translate('dashboard', 'size', data['lang']) }}</h6>
|
||||
<div draggable="false" id="m_server_world_{{server['server_data']['server_id']}}">
|
||||
{{ server['stats']['world_size'] }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6" style="width: auto;">
|
||||
<h6>{{ translate('dashboard', 'players', data['lang']) }}</h6>
|
||||
<div draggable="false" id="m_server_desc_{{server['server_data']['server_id']}}">
|
||||
{% if server['stats']['int_ping_results'] %}
|
||||
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard',
|
||||
'max',
|
||||
data['lang']) }} <br />
|
||||
|
||||
{% if server['stats']['desc'] != 'False' %}
|
||||
<div id="desc_id"
|
||||
style="overflow-wrap: break-word !important; max-width: 85px !important; overflow: scroll;">
|
||||
{{ server['stats']['desc'] }}</div> <br />
|
||||
{% end %}
|
||||
|
||||
{% if server['stats']['version'] != 'False' %}
|
||||
{{ server['stats']['version'] }}
|
||||
{% end %}
|
||||
{% end %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% end %}
|
||||
</div>
|
||||
</div>
|
||||
{% end %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -909,4 +965,4 @@
|
||||
|
||||
</script>
|
||||
|
||||
{% end %}
|
||||
{% end %}
|
@ -15,8 +15,12 @@
|
||||
<a class="dropdown-item {% if data['active_link'] == 'schedules' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=schedules" role="tab" aria-selected="false"><i class="fas fa-clock"></i> {{ translate('serverDetails', 'schedule', data['lang']) }}</a>
|
||||
{% end %}
|
||||
{% if data['permissions']['Backup'] in data['user_permissions'] %}
|
||||
{% if data['backup_failed'] %}
|
||||
<a style="color: red !important;" class="dropdown-item {% if data['active_link'] == 'backup' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false"><i class="fas fa-save"></i> {{ translate('serverDetails', 'backup', data['lang']) }} <i class="fas fa-exclamation-triangle"> </i></a>
|
||||
{% else %}
|
||||
<a class="dropdown-item {% if data['active_link'] == 'backup' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false"><i class="fas fa-save"></i> {{ translate('serverDetails', 'backup', data['lang']) }}</a>
|
||||
{% end %}
|
||||
{% end %}
|
||||
{% if data['permissions']['Files'] in data['user_permissions'] %}
|
||||
<a class="dropdown-item {% if data['active_link'] == 'files' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false"><i class="fas fa-folder-tree"></i> {{ translate('serverDetails', 'files', data['lang']) }}</a>
|
||||
{% end %}
|
||||
|
@ -19,11 +19,18 @@
|
||||
</li>
|
||||
{% end %}
|
||||
{% if data['permissions']['Backup'] in data['user_permissions'] %}
|
||||
{% if data['backup_failed'] %}
|
||||
<li class="nav-item term-nav-item">
|
||||
<a style="color: red !important;" class="nav-link {% if data['active_link'] == 'backup' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
|
||||
<i class="fas fa-save"></i>{{ translate('serverDetails', 'backup', data['lang']) }} <i class="fas fa-exclamation-triangle"> </i></a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item term-nav-item">
|
||||
<a class="nav-link {% if data['active_link'] == 'backup' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
|
||||
<i class="fas fa-save"></i>{{ translate('serverDetails', 'backup', data['lang']) }}</a>
|
||||
</li>
|
||||
{% end %}
|
||||
{% end %}
|
||||
{% if data['permissions']['Files'] in data['user_permissions'] %}
|
||||
<li class="nav-item term-nav-item">
|
||||
<a class="nav-link {% if data['active_link'] == 'files' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
|
||||
|
@ -14,7 +14,8 @@
|
||||
<div class="col-12">
|
||||
<div class="page-header">
|
||||
<h4 class="page-title">
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||
data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
</h4>
|
||||
@ -51,55 +52,85 @@
|
||||
|
||||
{% if data['backing_up'] %}
|
||||
<div class="progress" style="height: 15px;">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" id="backup_progress_bar" role="progressbar" style="width:{{data['backup_stats']['percent']}}%;" aria-valuenow="{{data['backup_stats']['percent']}}" aria-valuemin="0" aria-valuemax="100">{{ data['backup_stats']['percent'] }}%</div>
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" id="backup_progress_bar"
|
||||
role="progressbar" style="width:{{data['backup_stats']['percent']}}%;"
|
||||
aria-valuenow="{{data['backup_stats']['percent']}}" aria-valuemin="0" aria-valuemax="100">{{
|
||||
data['backup_stats']['percent'] }}%</div>
|
||||
</div>
|
||||
<p>Backing up <span id="total_files">{{data['backup_stats']['total_files']}}</span> Files</p>
|
||||
<p>Backing up <i class="fas fa-spin fa-spinner"></i> <span
|
||||
id="total_files">{{data['server_stats']['world_size']}}</span></p>
|
||||
{% end %}
|
||||
|
||||
<br>
|
||||
{% if not data['backing_up'] %}
|
||||
<div id="backup_button" class="form-group">
|
||||
<button class="btn btn-primary" id="backup_now_button">{{ translate('serverBackups', 'backupNow', data['lang']) }}</button>
|
||||
<button class="btn btn-primary" id="backup_now_button">{{ translate('serverBackups', 'backupNow',
|
||||
data['lang']) }}</button>
|
||||
</div>
|
||||
{% end %}
|
||||
<div class="form-group">
|
||||
{% if data['super_user'] %}
|
||||
<label for="server_name">{{ translate('serverBackups', 'storageLocation', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverBackups', 'storageLocationDesc', data['lang']) }}</small> </label>
|
||||
<input type="text" class="form-control" name="backup_path" id="backup_path" value="{{ data['server_stats']['server_id']['backup_path'] }}" placeholder="{{ translate('serverBackups', 'storageLocation', data['lang']) }}">
|
||||
<label for="server_name">{{ translate('serverBackups', 'storageLocation', data['lang']) }} <small
|
||||
class="text-muted ml-1"> - {{ translate('serverBackups', 'storageLocationDesc', data['lang'])
|
||||
}}</small> </label>
|
||||
<input type="text" class="form-control" name="backup_path" id="backup_path"
|
||||
value="{{ data['server_stats']['server_id']['backup_path'] }}"
|
||||
placeholder="{{ translate('serverBackups', 'storageLocation', data['lang']) }}">
|
||||
{% end %}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="server_path">{{ translate('serverBackups', 'maxBackups', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverBackups', 'maxBackupsDesc', data['lang']) }}</small> </label>
|
||||
<input type="text" class="form-control" name="max_backups" id="max_backups" value="{{ data['backup_config']['max_backups'] }}" placeholder="{{ translate('serverBackups', 'maxBackups', data['lang']) }}">
|
||||
<label for="server_path">{{ translate('serverBackups', 'maxBackups', data['lang']) }} <small
|
||||
class="text-muted ml-1"> - {{ translate('serverBackups', 'maxBackupsDesc', data['lang'])
|
||||
}}</small> </label>
|
||||
<input type="text" class="form-control" name="max_backups" id="max_backups"
|
||||
value="{{ data['backup_config']['max_backups'] }}"
|
||||
placeholder="{{ translate('serverBackups', 'maxBackups', data['lang']) }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="compress" class="form-check-label ml-4 mb-4"></label>
|
||||
{% if data['backup_config']['compress'] %}
|
||||
<input type="checkbox" class="form-check-input" id="compress" name="compress"
|
||||
checked="" value="True">{{ translate('serverBackups', 'compress', data['lang']) }}
|
||||
<input type="checkbox" class="form-check-input" id="compress" name="compress" checked=""
|
||||
value="True">{{ translate('serverBackups', 'compress', data['lang']) }}
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="compress" name="compress"
|
||||
value="True">{{ translate('serverBackups', 'compress', data['lang']) }}
|
||||
<input type="checkbox" class="form-check-input" id="compress" name="compress" value="True">{{
|
||||
translate('serverBackups', 'compress', data['lang']) }}
|
||||
{% end %}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="server">{{ translate('serverBackups', 'exclusionsTitle', data['lang']) }} <small> - {{ translate('serverBackups', 'excludedChoose', data['lang']) }}</small></label>
|
||||
<br>
|
||||
<button class="btn btn-primary mr-2" id="root_files_button" data-server_path="{{ data['server_stats']['server_id']['path']}}" type="button">{{ translate('serverBackups', 'clickExclude', data['lang']) }}</button>
|
||||
<label for="shutdown" class="form-check-label ml-4 mb-4"></label>
|
||||
{% if data['backup_config']['shutdown'] %}
|
||||
<input type="checkbox" class="form-check-input" id="shutdown" name="shutdown" checked=""
|
||||
value="True">{{ translate('serverBackups', 'shutdown', data['lang']) }}
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="shutdown" name="shutdown" value="True">{{
|
||||
translate('serverBackups', 'shutdown', data['lang']) }}
|
||||
{% end %}
|
||||
</div>
|
||||
<input type="number" class="form-control" name="changed" id="changed" value="0" style="visibility: hidden;"></input>
|
||||
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select" aria-hidden="true">
|
||||
<div class="form-group">
|
||||
<label for="server">{{ translate('serverBackups', 'exclusionsTitle', data['lang']) }} <small> - {{
|
||||
translate('serverBackups', 'excludedChoose', data['lang']) }}</small></label>
|
||||
<br>
|
||||
<button class="btn btn-primary mr-2" id="root_files_button"
|
||||
data-server_path="{{ data['server_stats']['server_id']['path']}}" type="button">{{
|
||||
translate('serverBackups', 'clickExclude', data['lang']) }}</button>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="changed" id="changed" value="0"
|
||||
style="visibility: hidden;"></input>
|
||||
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLongTitle">{{ translate('serverBackups', 'excludedChoose', data['lang']) }}</h5>
|
||||
<h5 class="modal-title" id="exampleModalLongTitle">{{ translate('serverBackups',
|
||||
'excludedChoose', data['lang']) }}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="tree-ctx-item" id="main-tree-div" data-path="" style="overflow: scroll; max-height:75%;">
|
||||
<div class="tree-ctx-item" id="main-tree-div" data-path=""
|
||||
style="overflow: scroll; max-height:75%;">
|
||||
<input type="checkbox" id="main-tree-input" name="root_path" value="" disabled>
|
||||
<span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path="">
|
||||
<i class="far fa-folder"></i>
|
||||
@ -110,15 +141,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" id="modal-cancel" class="btn btn-secondary" data-dismiss="modal">{{ translate('serverBackups', 'cancel', data['lang']) }}</button>
|
||||
<button type="button" id="modal-okay" data-dismiss="modal" class="btn btn-primary">{{ translate('serverWizard', 'save', data['lang']) }}</button>
|
||||
<button type="button" id="modal-cancel" class="btn btn-secondary" data-dismiss="modal">{{
|
||||
translate('serverBackups', 'cancel', data['lang']) }}</button>
|
||||
<button type="button" id="modal-okay" data-dismiss="modal" class="btn btn-primary">{{
|
||||
translate('serverWizard', 'save', data['lang']) }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success mr-2">{{ translate('serverBackups', 'save', data['lang']) }}</button>
|
||||
<button type="reset" class="btn btn-light">{{ translate('serverBackups', 'cancel', data['lang']) }}</button>
|
||||
<button type="submit" class="btn btn-success mr-2">{{ translate('serverBackups', 'save', data['lang'])
|
||||
}}</button>
|
||||
<button type="reset" class="btn btn-light">{{ translate('serverBackups', 'cancel', data['lang'])
|
||||
}}</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -138,13 +173,15 @@
|
||||
{% for backup in data['backup_list'] %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/panel/download_backup?file={{ backup['path'] }}&id={{ data['server_stats']['server_id']['server_id'] }}" class="btn btn-primary">
|
||||
<a href="/panel/download_backup?file={{ backup['path'] }}&id={{ data['server_stats']['server_id']['server_id'] }}"
|
||||
class="btn btn-primary">
|
||||
<i class="fas fa-download" aria-hidden="true"></i>
|
||||
{{ translate('serverBackups', 'download', data['lang']) }}
|
||||
</a>
|
||||
<br>
|
||||
<br>
|
||||
<button data-file="{{ backup['path'] }}" data-backup_path="{{ data['backup_path'] }}" class="btn btn-danger del_button">
|
||||
<button data-file="{{ backup['path'] }}" data-backup_path="{{ data['backup_path'] }}"
|
||||
class="btn btn-danger del_button">
|
||||
<i class="fas fa-trash" aria-hidden="true"></i>
|
||||
{{ translate('serverBackups', 'delete', data['lang']) }}
|
||||
</button>
|
||||
@ -168,7 +205,8 @@
|
||||
<br>
|
||||
<br>
|
||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||
<h4 class="card-title"><i class="fas fa-server"></i> {{ translate('serverBackups', 'excludedBackups', data['lang']) }} <small class="text-muted ml-1"></small> </h4>
|
||||
<h4 class="card-title"><i class="fas fa-server"></i> {{ translate('serverBackups', 'excludedBackups',
|
||||
data['lang']) }} <small class="text-muted ml-1"></small> </h4>
|
||||
</div>
|
||||
<br>
|
||||
<ul>
|
||||
|
@ -32,11 +32,16 @@
|
||||
<div class="col-sm-12 grid-margin">
|
||||
<div class="card">
|
||||
<div class="card-body pt-0">
|
||||
{% include "parts/server_controls_list.html" %}
|
||||
<span class="d-none d-sm-block">
|
||||
{% include "parts/server_controls_list.html %}
|
||||
</span>
|
||||
<span class="d-block d-sm-none">
|
||||
{% include "parts/m_server_controls_list.html %}
|
||||
</span>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<form class="forms-sample" method="post" action="/panel/server_detail">
|
||||
<form class="forms-sample" method="post" id="config_form" action="/panel/server_detail">
|
||||
{% raw xsrf_form_html() %}
|
||||
<input type="hidden" name="id" value="{{ data['server_stats']['server_id']['server_id'] }}">
|
||||
<input type="hidden" name="subpage" value="config">
|
||||
@ -50,8 +55,8 @@
|
||||
placeholder="{{ translate('serverConfig', 'serverName', data['lang']) }}" required>
|
||||
</div>
|
||||
|
||||
{% if data['super_user'] %}
|
||||
<div class="form-group">
|
||||
{% if data['super_user'] %}
|
||||
<label for="server_path">{{ translate('serverConfig', 'serverPath', data['lang']) }} <small
|
||||
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverPathDesc', data['lang']) }}</small>
|
||||
</label>
|
||||
@ -78,7 +83,25 @@
|
||||
value="{{ data['server_stats']['server_id']['executable'] }}"
|
||||
placeholder="{{ translate('serverConfig', 'serverExecutable', data['lang']) }}" required>
|
||||
</div>
|
||||
{% end %}
|
||||
{% if data['server_stats']['server_type'] == "minecraft-java" %}
|
||||
<div class="form-group">
|
||||
<label for="java_selection">{{ translate('serverConfig', 'javaVersion', data['lang']) }}
|
||||
<small class="text-muted ml-1">{{ translate('serverConfig', 'javaVersionDesc', data['lang'])
|
||||
}}</small>
|
||||
</label>
|
||||
<select class="form-select form-control form-control-lg select-css" id="java_selection"
|
||||
name="java_selection" form="config_form">
|
||||
<option value="">{{ translate('serverConfig',
|
||||
'javaNoChange', data['lang'])}}</option>
|
||||
{% for path in data['java_versions'] %}
|
||||
<option value="{{path}}">{{path}}</option>
|
||||
{% end %}
|
||||
</select>
|
||||
</div>
|
||||
{% end %}
|
||||
|
||||
{% if data['super_user'] %}
|
||||
<div class="form-group">
|
||||
<label for="execution_command">{{ translate('serverConfig', 'serverExecutionCommand', data['lang']) }}
|
||||
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverExecutionCommandDesc',
|
||||
@ -86,91 +109,97 @@
|
||||
<input type="text" class="form-control" name="execution_command" id="execution_command"
|
||||
value="{{ data['server_stats']['server_id']['execution_command'] }}"
|
||||
placeholder="{{ translate('serverConfig', 'serverExecutionCommand', data['lang']) }}" required>
|
||||
</div>
|
||||
{% else %}
|
||||
<label for="execution_command">{{ translate('serverConfig', 'serverExecutionCommand', data['lang']) }}
|
||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||
<span style="color: gray;">{{ data['server_stats']['server_id']['execution_command'] }}</span> 🔒
|
||||
</div>
|
||||
<br>
|
||||
{% end %}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="stop_command">{{ translate('serverConfig', 'serverStopCommand', data['lang']) }} <small
|
||||
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverStopCommandDesc', data['lang'])
|
||||
}}</small> </label>
|
||||
<input type="text" class="form-control" name="stop_command" id="stop_command"
|
||||
value="{{ data['server_stats']['server_id']['stop_command'] }}"
|
||||
placeholder="{{ translate('serverConfig', 'serverStopCommand', data['lang']) }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="stop_command">{{ translate('serverConfig', 'serverStopCommand', data['lang']) }} <small
|
||||
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverStopCommandDesc', data['lang'])
|
||||
}}</small> </label>
|
||||
<input type="text" class="form-control" name="stop_command" id="stop_command"
|
||||
value="{{ data['server_stats']['server_id']['stop_command'] }}"
|
||||
placeholder="{{ translate('serverConfig', 'serverStopCommand', data['lang']) }}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="auto_start_delay">{{ translate('serverConfig', 'serverAutostartDelay', data['lang']) }}
|
||||
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverAutostartDelayDesc',
|
||||
data['lang']) }}</small> </label>
|
||||
<input type="number" class="form-control" name="auto_start_delay" id="auto_start_delay"
|
||||
value="{{ data['server_stats']['server_id']['auto_start_delay'] }}" step="1" max="999" min="10"
|
||||
required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="auto_start_delay">{{ translate('serverConfig', 'serverAutostartDelay', data['lang']) }}
|
||||
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverAutostartDelayDesc',
|
||||
data['lang']) }}</small> </label>
|
||||
<input type="number" class="form-control" name="auto_start_delay" id="auto_start_delay"
|
||||
value="{{ data['server_stats']['server_id']['auto_start_delay'] }}" step="1" max="999" min="10"
|
||||
required>
|
||||
</div>
|
||||
|
||||
{% if data['super_user'] %}
|
||||
<div class="form-group">
|
||||
<label for="executable_update_url">{{ translate('serverConfig', 'exeUpdateURL', data['lang']) }}
|
||||
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'exeUpdateURLDesc', data['lang'])
|
||||
}}</small> </label>
|
||||
<input type="text" class="form-control" name="executable_update_url" id="executable_update_url"
|
||||
value="{{ data['server_stats']['server_id']['executable_update_url'] }}"
|
||||
placeholder="{{ translate('serverConfig', 'exeUpdateURL', data['lang']) }}">
|
||||
</div>
|
||||
{% if data['super_user'] %}
|
||||
<div class="form-group">
|
||||
<label for="executable_update_url">{{ translate('serverConfig', 'exeUpdateURL', data['lang']) }}
|
||||
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'exeUpdateURLDesc', data['lang'])
|
||||
}}</small> </label>
|
||||
<input type="text" class="form-control" name="executable_update_url" id="executable_update_url"
|
||||
value="{{ data['server_stats']['server_id']['executable_update_url'] }}"
|
||||
placeholder="{{ translate('serverConfig', 'exeUpdateURL', data['lang']) }}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="server_ip">{{ translate('serverConfig', 'serverIP', data['lang']) }} <small
|
||||
class="text-muted ml-1">- {{ translate('serverConfig', 'serverIPDesc', data['lang']) }}</small>
|
||||
</label>
|
||||
<input type="text" class="form-control" name="server_ip" id="server_ip"
|
||||
value="{{ data['server_stats']['server_id']['server_ip'] }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="server_ip">{{ translate('serverConfig', 'serverIP', data['lang']) }} <small
|
||||
class="text-muted ml-1">- {{ translate('serverConfig', 'serverIPDesc', data['lang']) }}</small>
|
||||
</label>
|
||||
<input type="text" class="form-control" name="server_ip" id="server_ip"
|
||||
value="{{ data['server_stats']['server_id']['server_ip'] }}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="server_port">{{ translate('serverConfig', 'serverPort', data['lang']) }} <small
|
||||
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverPortDesc', data['lang']) }}
|
||||
</small> </label>
|
||||
<input type="number" class="form-control" name="server_port" id="server_port"
|
||||
value="{{ data['server_stats']['server_id']['server_port'] }}" step="1" max="65566" min="1"
|
||||
required>
|
||||
</div>
|
||||
{% end %}
|
||||
<div class="form-group">
|
||||
<label for="server_port">{{ translate('serverConfig', 'serverPort', data['lang']) }} <small
|
||||
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverPortDesc', data['lang']) }}
|
||||
</small> </label>
|
||||
<input type="number" class="form-control" name="server_port" id="server_port"
|
||||
value="{{ data['server_stats']['server_id']['server_port'] }}" step="1" max="65566" min="1"
|
||||
required>
|
||||
</div>
|
||||
{% end %}
|
||||
|
||||
<div class="form-group">
|
||||
<label for="logs_delete_after">{{ translate('serverConfig', 'removeOldLogsAfter', data['lang']) }}
|
||||
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'removeOldLogsAfterDesc',
|
||||
data['lang']) }}</small> </label>
|
||||
<input type="number" class="form-control" name="logs_delete_after" id="logs_delete_after"
|
||||
value="{{ data['server_stats']['server_id']['logs_delete_after'] }}" step="1" max="365" min="0"
|
||||
required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="logs_delete_after">{{ translate('serverConfig', 'removeOldLogsAfter', data['lang']) }}
|
||||
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'removeOldLogsAfterDesc',
|
||||
data['lang']) }}</small> </label>
|
||||
<input type="number" class="form-control" name="logs_delete_after" id="logs_delete_after"
|
||||
value="{{ data['server_stats']['server_id']['logs_delete_after'] }}" step="1" max="365" min="0"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="form-check-flat">
|
||||
<label for="auto_start" class="form-check-label ml-4 mb-4">
|
||||
{% if data['server_stats']['server_id']['auto_start'] %}
|
||||
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" checked=""
|
||||
value="1">{{ translate('serverConfig', 'serverAutoStart', data['lang']) }}
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" value="1">{{
|
||||
translate('serverConfig', 'serverAutoStart', data['lang']) }}
|
||||
{% end %}
|
||||
</label>
|
||||
<div class="form-check-flat">
|
||||
<label for="auto_start" class="form-check-label ml-4 mb-4">
|
||||
{% if data['server_stats']['server_id']['auto_start'] %}
|
||||
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" checked=""
|
||||
value="1">{{ translate('serverConfig', 'serverAutoStart', data['lang']) }}
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" value="1">{{
|
||||
translate('serverConfig', 'serverAutoStart', data['lang']) }}
|
||||
{% end %}
|
||||
</label>
|
||||
|
||||
<label for="crash_detection" class="form-check-label ml-4 mb-4">
|
||||
{% if data['server_stats']['server_id']['crash_detection'] %}
|
||||
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection"
|
||||
checked="" value="1">{{ translate('serverConfig', 'serverCrashDetection', data['lang']) }}
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection"
|
||||
value="1">{{ translate('serverConfig', 'serverCrashDetection', data['lang']) }}
|
||||
{% end %}
|
||||
</label>
|
||||
<label for="crash_detection" class="form-check-label ml-4 mb-4">
|
||||
{% if data['server_stats']['server_id']['crash_detection'] %}
|
||||
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection"
|
||||
checked="" value="1">{{ translate('serverConfig', 'serverCrashDetection', data['lang']) }}
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection"
|
||||
value="1">{{ translate('serverConfig', 'serverCrashDetection', data['lang']) }}
|
||||
{% end %}
|
||||
</label>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{
|
||||
translate('serverConfig', 'save', data['lang']) }}</button>
|
||||
<button type="reset" class="btn btn-light"><i class="fas fa-times"></i> {{ translate('serverConfig',
|
||||
'cancel', data['lang']) }}</button>
|
||||
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{
|
||||
translate('serverConfig', 'save', data['lang']) }}</button>
|
||||
<button type="reset" class="btn btn-light"><i class="fas fa-times"></i> {{ translate('serverConfig',
|
||||
'cancel', data['lang']) }}</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -373,4 +402,4 @@
|
||||
|
||||
</script>
|
||||
|
||||
{% end %}
|
||||
{% end %}
|
@ -32,7 +32,12 @@
|
||||
<div class="col-sm-12 grid-margin">
|
||||
<div class="card">
|
||||
<div class="card-body pt-0">
|
||||
{% include "parts/server_controls_list.html" %}
|
||||
<span class="d-none d-sm-block">
|
||||
{% include "parts/server_controls_list.html %}
|
||||
</span>
|
||||
<span class="d-block d-sm-none">
|
||||
{% include "parts/m_server_controls_list.html %}
|
||||
</span>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
@ -212,7 +217,9 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
html, body, body > .container-scroller {
|
||||
html,
|
||||
body,
|
||||
body>.container-scroller {
|
||||
overflow: initial;
|
||||
}
|
||||
|
||||
@ -221,7 +228,9 @@
|
||||
position: sticky;
|
||||
}
|
||||
</style>
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<div id="editor_container" class="col-md-6 col-sm-12">
|
||||
<br>
|
||||
<br>
|
||||
<div class="editorManager">
|
||||
<h2 id="fileError"></h2>
|
||||
<div id="editorParent">
|
||||
@ -239,6 +248,9 @@
|
||||
data-handler-name="ace/keyboard/emacs">Emacs</button>
|
||||
<button onclick="setKeyboard(event.target)" class="btn btn-secondary"
|
||||
data-handler-name="ace/keyboard/sublime">Sublime</button>
|
||||
<span class="d-none d-md-block "> <button class="btn btn-info" id="screen-size">{{
|
||||
translate('serverFiles',
|
||||
'size', data['lang']) }}</button></span>
|
||||
</div>
|
||||
<h3 id="file_warn"></h3>
|
||||
<button class="btn btn-success" onclick="save()"><i class="fas fa-save"></i> {{ translate('serverFiles',
|
||||
@ -285,7 +297,7 @@
|
||||
mac: 'Command-S',
|
||||
sender: 'editor|cli'
|
||||
},
|
||||
exec: function(env, args, request) {
|
||||
exec: function (env, args, request) {
|
||||
save()
|
||||
}
|
||||
});
|
||||
@ -408,6 +420,7 @@
|
||||
setFileName(event.target.innerText);
|
||||
editor.session.setValue(json.content);
|
||||
serverFileContent = json.content;
|
||||
setSaveStatus(true);
|
||||
}
|
||||
},
|
||||
});
|
||||
@ -530,8 +543,10 @@
|
||||
document.getElementById('save_status').style.color = saved ? '#2fb689' : 'gray';
|
||||
}
|
||||
['change', 'undo', 'redo'].forEach(event => editor.on(event, (event) => setSaveStatus(serverFileContent === editor.session.getValue())))
|
||||
$('#screen-size').on('click', function (e) {
|
||||
$('#editor_container').toggleClass(`col-md-6`)
|
||||
|
||||
|
||||
});
|
||||
setFileName();
|
||||
$('#editorParent').toggle(false) // show
|
||||
$('#fileError').toggle(false) // hide
|
||||
@ -1097,7 +1112,7 @@
|
||||
if (handlerName == 'null') handlerName = null;
|
||||
editor.setKeyboardHandler(handlerName, () => {
|
||||
if (handlerName == 'ace/keyboard/vim') {
|
||||
require("ace/keyboard/vim").Vim.defineEx('write', 'w', function() {
|
||||
require("ace/keyboard/vim").Vim.defineEx('write', 'w', function () {
|
||||
save();
|
||||
});
|
||||
}
|
||||
@ -1117,4 +1132,4 @@
|
||||
|
||||
</script>
|
||||
|
||||
{% end %}
|
||||
{% end %}
|
@ -9,14 +9,15 @@
|
||||
|
||||
<div class="content-wrapper">
|
||||
|
||||
<!-- Page Title Header Starts-->
|
||||
<!-- Page Title Header Starts-->
|
||||
<div class="row page-title-header">
|
||||
<div class="col-12">
|
||||
<div class="page-header">
|
||||
<h4 class="page-title">
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||
data['server_stats']['server_id']['server_name'] }}
|
||||
<br />
|
||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
@ -24,23 +25,30 @@
|
||||
</div>
|
||||
<!-- Page Title Header Ends-->
|
||||
|
||||
{% include "parts/details_stats.html" %}
|
||||
{% include "parts/details_stats.html" %}
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12 grid-margin">
|
||||
<div class="card">
|
||||
<div class="card-body pt-0">
|
||||
{% include "parts/server_controls_list.html" %}
|
||||
<span class="d-none d-sm-block">
|
||||
{% include "parts/server_controls_list.html %}
|
||||
</span>
|
||||
<span class="d-block d-sm-none">
|
||||
{% include "parts/m_server_controls_list.html %}
|
||||
</span>
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="input-group">
|
||||
<div id="virt_console" class="" style="font-size: .8em; padding: 5px 10px; border: 1px solid #383e5d; background-color:#2a2c44;height:500px; overflow: scroll;"></div>
|
||||
<div class="col-md-12">
|
||||
<div class="input-group">
|
||||
<div id="virt_console" class=""
|
||||
style="font-size: .8em; padding: 5px 10px; border: 1px solid #383e5d; background-color:#2a2c44;height:500px; overflow: scroll;">
|
||||
</div>
|
||||
<br />
|
||||
|
||||
|
||||
</div>
|
||||
<br />
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@ -57,8 +65,8 @@
|
||||
<script>
|
||||
|
||||
const serverId = new URLSearchParams(document.location.search).get('id')
|
||||
function get_server_log(){
|
||||
if( !$("#stop_scroll").is(':checked')){
|
||||
function get_server_log() {
|
||||
if (!$("#stop_scroll").is(':checked')) {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: '/ajax/server_log?id=' + serverId + '&full=1',
|
||||
@ -78,21 +86,21 @@
|
||||
return r ? r[1] : undefined;
|
||||
}
|
||||
|
||||
$( document ).ready(function() {
|
||||
console.log( "ready!" );
|
||||
$(document).ready(function () {
|
||||
console.log("ready!");
|
||||
get_server_log()
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
function scroll(){
|
||||
function scroll() {
|
||||
var logview = $('#virt_console');
|
||||
if(logview.length)
|
||||
if (logview.length)
|
||||
logview.scrollTop(logview[0].scrollHeight - logview.height());
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
{% end %}
|
||||
{% end %}
|
@ -34,7 +34,7 @@
|
||||
<div class="form-group">
|
||||
<label for="server_type">{{ translate('serverWizard', 'serverType', data['lang']) }}</label>
|
||||
<select required class="form-control form-control-lg select-css" id="server_type" name="server_type"
|
||||
onchange="serverTypeChange(this)">
|
||||
onchange="serverTypeChange(this)">
|
||||
<option value="">{{ translate('serverWizard', 'selectType', data['lang']) }}</option>
|
||||
{% for s in data['server_types'] %}
|
||||
<option value="{{ s }}">{{ s.capitalize() }}</option>
|
||||
@ -56,42 +56,42 @@
|
||||
<div class="form-group">
|
||||
<label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label>
|
||||
<input type="text" class="form-control" id="server_name" name="server_name"
|
||||
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
|
||||
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<br />
|
||||
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small
|
||||
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
|
||||
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
|
||||
data['lang']) }}</small></h4>
|
||||
<hr>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-3">
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group">
|
||||
<label for="min_memory1">{{ translate('serverWizard', 'minMem', data['lang']) }} <small> - {{
|
||||
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
||||
<input type="number" class="form-control" id="min_memory1" name="min_memory" value="1" step="0.5"
|
||||
min="0.5" required>
|
||||
min="0.5" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3 offset-1">
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group">
|
||||
<label for="max_memory1">{{ translate('serverWizard', 'maxMem', data['lang']) }} <small> - {{
|
||||
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
||||
<input type="number" class="form-control" id="max_memory1" name="max_memory" value="2" step="0.5"
|
||||
min="0.5" required>
|
||||
min="0.5" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3 offset-1">
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group">
|
||||
<label for="port1">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small> - {{
|
||||
translate('serverWizard', 'defaultPort', data['lang']) }}</small></label>
|
||||
<input type="number" class="form-control" id="port1" name="port" value="25565" step="1" min="1"
|
||||
required>
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
@ -100,7 +100,7 @@
|
||||
<div class="card">
|
||||
<div class="card-header p-2" id="Role-1">
|
||||
<p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-1" aria-expanded="true"
|
||||
aria-controls="collapseRole-1">
|
||||
aria-controls="collapseRole-1">
|
||||
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang']) }}
|
||||
<small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate',
|
||||
data['lang']) }}</small>
|
||||
@ -111,7 +111,7 @@
|
||||
<div class="form-group">
|
||||
{% for r in data['roles'] %}
|
||||
<span class="d-block menu-option"><label><input name="{{ r['role_id'] }}"
|
||||
type="checkbox">
|
||||
type="checkbox">
|
||||
{{ r['role_name'].capitalize() }}</label></span>
|
||||
{% end %}
|
||||
</div>
|
||||
@ -152,7 +152,7 @@
|
||||
<div class="form-group">
|
||||
<label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label>
|
||||
<input type="text" class="form-control" id="server_name" name="server_name" value=""
|
||||
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
|
||||
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -161,7 +161,7 @@
|
||||
<label for="server">{{ translate('serverWizard', 'serverPath', data['lang']) }} <small>{{
|
||||
translate('serverWizard', 'absoluteServerPath', data['lang']) }}</small></label>
|
||||
<input type="text" class="form-control" id="server_path" name="server_path"
|
||||
placeholder="/var/opt/server" required>
|
||||
placeholder="/var/opt/server" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -169,7 +169,7 @@
|
||||
<div class="form-group">
|
||||
<label for="server_jar">{{ translate('serverWizard', 'serverJar', data['lang']) }}</label>
|
||||
<input type="text" class="form-control" id="server_jar" name="server_jar" value=""
|
||||
placeholder="paper.jar" required>
|
||||
placeholder="paper.jar" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -178,35 +178,35 @@
|
||||
</div>
|
||||
<br />
|
||||
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small
|
||||
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
|
||||
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
|
||||
data['lang']) }}</small></h4>
|
||||
<hr>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-3">
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group">
|
||||
<label for="min_memory2">{{ translate('serverWizard', 'minMem', data['lang']) }} <small> - {{
|
||||
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
||||
<input type="number" class="form-control" id="min_memory2" name="min_memory" value="1" step="0.5"
|
||||
min="0.5" required>
|
||||
min="0.5" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3 offset-1">
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group">
|
||||
<label for="max_memory2">{{ translate('serverWizard', 'maxMem', data['lang']) }} <small> - {{
|
||||
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
||||
<input type="number" class="form-control" id="max_memory2" name="max_memory" value="2" step="0.5"
|
||||
min="0.5" required>
|
||||
min="0.5" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3 offset-1">
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group">
|
||||
<label for="port2">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small> - {{
|
||||
translate('serverWizard', 'defaultPort', data['lang']) }}</small></label>
|
||||
<input type="number" class="form-control" id="port2" name="port" value="25565" step="1" min="1"
|
||||
required>
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
@ -215,7 +215,7 @@
|
||||
<div class="card">
|
||||
<div class="card-header p-2" id="Role-2">
|
||||
<p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-2" aria-expanded="true"
|
||||
aria-controls="collapseRole-2">
|
||||
aria-controls="collapseRole-2">
|
||||
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang']) }}
|
||||
<small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate',
|
||||
data['lang']) }}</small>
|
||||
@ -226,7 +226,7 @@
|
||||
<div class="form-group">
|
||||
{% for r in data['roles'] %}
|
||||
<span class="d-block menu-option"><label><input name="{{ r['role_id'] }}"
|
||||
type="checkbox">
|
||||
type="checkbox">
|
||||
{{ r['role_name'].capitalize() }}</label></span>
|
||||
{% end %}
|
||||
</div>
|
||||
@ -266,7 +266,7 @@
|
||||
<div class="form-group">
|
||||
<label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label>
|
||||
<input type="text" class="form-control" id="server_name" name="server_name" value=""
|
||||
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
|
||||
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -275,7 +275,7 @@
|
||||
<label for="server">{{ translate('serverWizard', 'zipPath', data['lang']) }} <small>{{
|
||||
translate('serverWizard', 'absoluteZipPath', data['lang']) }}</small></label>
|
||||
<input type="text" class="form-control" id="server_path" name="server_path"
|
||||
placeholder="/var/opt/server.zip" required>
|
||||
placeholder="/var/opt/server.zip" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -294,7 +294,7 @@
|
||||
<div class="form-group">
|
||||
<label for="server_jar">{{ translate('serverWizard', 'serverJar', data['lang']) }}</label>
|
||||
<input type="text" class="form-control" id="server_jar" name="server_jar" value=""
|
||||
placeholder="paper.jar" required>
|
||||
placeholder="paper.jar" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -303,7 +303,7 @@
|
||||
|
||||
<div class="col-sm-3">
|
||||
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small
|
||||
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
|
||||
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
|
||||
data['lang']) }}</small></h4>
|
||||
<hr>
|
||||
<div class="row">
|
||||
@ -313,7 +313,7 @@
|
||||
<label for="min_memory3">{{ translate('serverWizard', 'minMem', data['lang']) }} <small> - {{
|
||||
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
||||
<input type="number" class="form-control" id="min_memory3" name="min_memory" value="1" step="0.5"
|
||||
min="0.5" required>
|
||||
min="0.5" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -322,7 +322,7 @@
|
||||
<label for="max_memory3">{{ translate('serverWizard', 'maxMem', data['lang']) }} <small> - {{
|
||||
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
||||
<input type="number" class="form-control" id="max_memory3" name="max_memory" value="2" step="0.5"
|
||||
min="0.5" required>
|
||||
min="0.5" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -331,7 +331,7 @@
|
||||
<label for="port3">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small> - {{
|
||||
translate('serverWizard', 'defaultPort', data['lang']) }}</small></label>
|
||||
<input type="number" class="form-control" id="port3" name="port" value="25565" step="1" min="1"
|
||||
required>
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -341,7 +341,7 @@
|
||||
<div class="card">
|
||||
<div class="card-header p-2" id="Role-3">
|
||||
<p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-3"
|
||||
aria-expanded="true" aria-controls="collapseRole-3">
|
||||
aria-expanded="true" aria-controls="collapseRole-3">
|
||||
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang'])
|
||||
}} <small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate',
|
||||
data['lang']) }}</small>
|
||||
@ -352,7 +352,7 @@
|
||||
<div class="form-group">
|
||||
{% for r in data['roles'] %}
|
||||
<span class="d-block menu-option"><label><input name="{{ r['role_id'] }}"
|
||||
type="checkbox">
|
||||
type="checkbox">
|
||||
{{ r['role_name'].capitalize() }}</label></span>
|
||||
{% end %}
|
||||
</div>
|
||||
@ -368,7 +368,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@ -380,7 +380,7 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="tree-ctx-item" id="main-tree-div" data-path=""
|
||||
style="overflow: scroll; max-height:75%;">
|
||||
style="overflow: scroll; max-height:75%;">
|
||||
<input type="radio" id="main-tree-input" name="root_path" value="" checked>
|
||||
<span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path="">
|
||||
<i class="far fa-folder"></i>
|
||||
@ -401,7 +401,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<button id="zip_submit" type="submit" title="You must select server root dir first" disabled
|
||||
class="btn btn-primary mr-2">{{ translate('serverWizard', 'importServerButton', data['lang'])
|
||||
class="btn btn-primary mr-2">{{ translate('serverWizard', 'importServerButton', data['lang'])
|
||||
}}</button>
|
||||
<button type="reset" class="btn btn-danger mr-2">{{ translate('serverWizard', 'resetForm', data['lang'])
|
||||
}}</button>
|
||||
@ -710,4 +710,4 @@
|
||||
})
|
||||
}
|
||||
</script>
|
||||
{% end %}
|
||||
{% end %}
|
16
app/migrations/20220618_crafty_api_secret.py
Normal file
16
app/migrations/20220618_crafty_api_secret.py
Normal file
@ -0,0 +1,16 @@
|
||||
import peewee
|
||||
import datetime
|
||||
|
||||
|
||||
def migrate(migrator, db):
|
||||
class CraftySettings(peewee.Model):
|
||||
secret_api_key = peewee.CharField(default="")
|
||||
|
||||
class Meta:
|
||||
table_name = "crafty_settings"
|
||||
|
||||
migrator.create_table(CraftySettings)
|
||||
|
||||
|
||||
def rollback(migrator, db):
|
||||
migrator.drop_table("crafty_settings")
|
16
app/migrations/20220620_backup_shutdown.py
Normal file
16
app/migrations/20220620_backup_shutdown.py
Normal file
@ -0,0 +1,16 @@
|
||||
# Generated by database migrator
|
||||
import peewee
|
||||
|
||||
|
||||
def migrate(migrator, database, **kwargs):
|
||||
migrator.add_columns("backups", shutdown=peewee.BooleanField(default=False))
|
||||
"""
|
||||
Write your migrations here.
|
||||
"""
|
||||
|
||||
|
||||
def rollback(migrator, database, **kwargs):
|
||||
migrator.drop_columns("backups", ["shutdown"])
|
||||
"""
|
||||
Write your rollback migrations here.
|
||||
"""
|
@ -352,7 +352,7 @@
|
||||
"createDirQuestion": "Welchen Namen wünschen Sie für das neue Verzeichnis?",
|
||||
"createFile": "Datei erstellen",
|
||||
"createFileQuestion": "Welchen Namen wünschen Sie für die neue Datei?",
|
||||
"default": "Standart",
|
||||
"default": "Standard",
|
||||
"delete": "Löschen",
|
||||
"deleteItemQuestion": "Sind Sie sicher, dass Sie \" + name + \" löschen wollen?",
|
||||
"deleteItemQuestionMessage": "Sie löschen gerade \\\"\" + path + \"\\\"!<br/><br/> Diese Aktion ist unumkehrbar!",
|
||||
@ -454,7 +454,7 @@
|
||||
"buildServer": "Server erstellen!",
|
||||
"clickRoot": "Hier klicken, um das root Verzeichniss auszuwählen",
|
||||
"close": "Schließen",
|
||||
"defaultPort": "25565 (Standart)",
|
||||
"defaultPort": "25565 (Standard)",
|
||||
"downloading": "Server herunterladen...",
|
||||
"explainRoot": "Bitte klicken Sie auf die Schaltfläche unterhalb, um das Stammverzeichnis Ihres Servers innerhalb des Archivs auszuwählen",
|
||||
"importing": "Server importieren...",
|
||||
@ -529,4 +529,4 @@
|
||||
"userSettings": "Benutzereinstellungen",
|
||||
"uses": "Anzahl der erlaubten Verwendungen (-1==Keine Begrenzung)"
|
||||
}
|
||||
}
|
||||
}
|
@ -271,7 +271,8 @@
|
||||
"save": "Save",
|
||||
"size": "Size",
|
||||
"storageLocation": "Storage Location",
|
||||
"storageLocationDesc": "Where do you want to store backups?"
|
||||
"storageLocationDesc": "Where do you want to store backups?",
|
||||
"shutdown": "Shutdown server for duration of backup"
|
||||
},
|
||||
"serverConfig": {
|
||||
"bePatientDelete": "Please be patient while we remove your server from the Crafty panel. This screen will close in a few moments.",
|
||||
@ -300,6 +301,9 @@
|
||||
"serverCrashDetection": "Server Crash Detection",
|
||||
"serverExecutable": "Server Executable",
|
||||
"serverExecutableDesc": "The server's executable file",
|
||||
"javaVersion": "Override current Java Version",
|
||||
"javaVersionDesc": "If you're going to override java. Make sure your current java path in 'execution command' is wrapped in quotes (default 'java' variable excluded)",
|
||||
"javaNoChange": "Do Not Override",
|
||||
"serverExecutionCommand": "Server Execution Command",
|
||||
"serverExecutionCommandDesc": "What will be launched in a hidden terminal",
|
||||
"serverIP": "Server IP",
|
||||
@ -368,6 +372,7 @@
|
||||
"noscript": "The file manager does not work without JavaScript",
|
||||
"rename": "Rename",
|
||||
"renameItemQuestion": "What should the new name be?",
|
||||
"size": "Toggle Editor Size",
|
||||
"save": "Save",
|
||||
"stayHere": "DO NOT LEAVE THIS PAGE!",
|
||||
"unsupportedLanguage": "Warning: This is not a supported file type",
|
||||
@ -530,4 +535,4 @@
|
||||
"userSettings": "User Settings",
|
||||
"uses": "Number of uses allowed (-1==No Limit)"
|
||||
}
|
||||
}
|
||||
}
|
5
main.py
5
main.py
@ -7,6 +7,7 @@ import argparse
|
||||
import logging.config
|
||||
import signal
|
||||
import peewee
|
||||
from app.classes.shared.file_helpers import FileHelpers
|
||||
|
||||
from app.classes.shared.import3 import Import3
|
||||
from app.classes.shared.console import Console
|
||||
@ -132,9 +133,9 @@ if __name__ == "__main__":
|
||||
installer.default_settings()
|
||||
else:
|
||||
Console.debug("Existing install detected")
|
||||
|
||||
file_helper = FileHelpers(helper)
|
||||
# now the tables are created, we can load the tasks_manager and server controller
|
||||
controller = Controller(database, helper)
|
||||
controller = Controller(database, helper, file_helper)
|
||||
import3 = Import3(helper, controller)
|
||||
tasks_manager = TasksManager(helper, controller)
|
||||
tasks_manager.start_webserver()
|
||||
|
Loading…
Reference in New Issue
Block a user