mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'dev' into refactor/config-json
This commit is contained in:
commit
aa2ba4f8cf
@ -29,6 +29,7 @@ docker-build-dev:
|
|||||||
- docker run --rm --privileged aptman/qus -- -r
|
- docker run --rm --privileged aptman/qus -- -r
|
||||||
- docker run --rm --privileged aptman/qus -s -- -p aarch64 x86_64
|
- docker run --rm --privileged aptman/qus -s -- -p aarch64 x86_64
|
||||||
- echo $CI_BUILD_TOKEN | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
|
- echo $CI_BUILD_TOKEN | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
|
||||||
|
- echo $DOCKERHUB_TOKEN | docker login -u "$DOCKERHUB_USER" --password-stdin $DOCKERHUB_REGISTRY
|
||||||
script:
|
script:
|
||||||
- |
|
- |
|
||||||
tag=":$CI_COMMIT_REF_SLUG"
|
tag=":$CI_COMMIT_REF_SLUG"
|
||||||
@ -45,6 +46,7 @@ docker-build-dev:
|
|||||||
--build-arg "BUILD_REF=${CI_COMMIT_SHA}"
|
--build-arg "BUILD_REF=${CI_COMMIT_SHA}"
|
||||||
--build-arg "CRAFTY_VER=${VERSION}"
|
--build-arg "CRAFTY_VER=${VERSION}"
|
||||||
--tag "$CI_REGISTRY_IMAGE${tag}"
|
--tag "$CI_REGISTRY_IMAGE${tag}"
|
||||||
|
--tag "arcadiatechnology/crafty-4${tag}"
|
||||||
--platform linux/arm64/v8,linux/amd64
|
--platform linux/arm64/v8,linux/amd64
|
||||||
--push .
|
--push .
|
||||||
after_script:
|
after_script:
|
||||||
@ -83,6 +85,7 @@ docker-build-prod:
|
|||||||
- docker run --rm --privileged aptman/qus -- -r
|
- docker run --rm --privileged aptman/qus -- -r
|
||||||
- docker run --rm --privileged aptman/qus -s -- -p aarch64 x86_64
|
- docker run --rm --privileged aptman/qus -s -- -p aarch64 x86_64
|
||||||
- echo $CI_BUILD_TOKEN | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
|
- echo $CI_BUILD_TOKEN | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
|
||||||
|
- echo $DOCKERHUB_TOKEN | docker login -u "$DOCKERHUB_USER" --password-stdin $DOCKERHUB_REGISTRY
|
||||||
script:
|
script:
|
||||||
- |
|
- |
|
||||||
VERSION="${MAJOR}.${MINOR}.${SUB}"
|
VERSION="${MAJOR}.${MINOR}.${SUB}"
|
||||||
@ -99,6 +102,8 @@ docker-build-prod:
|
|||||||
--build-arg "CRAFTY_VER=${VERSION}"
|
--build-arg "CRAFTY_VER=${VERSION}"
|
||||||
--tag "$CI_REGISTRY_IMAGE:$VERSION"
|
--tag "$CI_REGISTRY_IMAGE:$VERSION"
|
||||||
--tag "$CI_REGISTRY_IMAGE:latest"
|
--tag "$CI_REGISTRY_IMAGE:latest"
|
||||||
|
--tag "arcadiatechnology/crafty-4:$VERSION"
|
||||||
|
--tag "arcadiatechnology/crafty-4:latest"
|
||||||
--platform linux/arm64/v8,linux/amd64
|
--platform linux/arm64/v8,linux/amd64
|
||||||
--push .
|
--push .
|
||||||
after_script:
|
after_script:
|
||||||
|
27
CHANGELOG.md
27
CHANGELOG.md
@ -1,7 +1,23 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
## --- [4.0.19] - 2022/TBD
|
## --- [4.0.20] - 2022/TBD
|
||||||
### New features
|
### New features
|
||||||
|
- Add option to run command before backup. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/536))
|
||||||
|
- Make Config.json editable from panel. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/532))
|
||||||
|
### Bug fixes
|
||||||
|
- Fix local java server imports. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/529))
|
||||||
|
- Fix Schedule Restore | Add Backup Config Preservation. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/533))
|
||||||
|
- Rework `/public` Route. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/538))
|
||||||
|
### Tweaks
|
||||||
|
- Hide stats DB directory from files tree. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/530))
|
||||||
|
- Added further login screen customisation settings. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/531))
|
||||||
|
- Set backup filename to use same time as schedule. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/534))
|
||||||
|
- Move Schedules to from DB to Queue Datatype. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/535))
|
||||||
|
- Move raknet icon failure to a debug log. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/537))
|
||||||
|
### Lang
|
||||||
TBD
|
TBD
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
## --- [4.0.19] - 2022/01/07
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
- Fix port tooltip not showing on dash while server online. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/503))
|
- Fix port tooltip not showing on dash while server online. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/503))
|
||||||
- Fix '+' char in path causing any file operation to fail. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/502))
|
- Fix '+' char in path causing any file operation to fail. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/502))
|
||||||
@ -12,11 +28,18 @@ TBD
|
|||||||
- Fix root dir selection in Upload Zip Import ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/508))
|
- Fix root dir selection in Upload Zip Import ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/508))
|
||||||
- Fix stats error on mac M1 chips ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/512))
|
- Fix stats error on mac M1 chips ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/512))
|
||||||
- Fix window path escape on java override ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/513))
|
- Fix window path escape on java override ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/513))
|
||||||
|
- Fix Forge import stalling on 1.17 Forge servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/515))
|
||||||
|
- Fix issue with server config for SU Accounts ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/516))
|
||||||
|
- Fix Nested reaction tasks ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/521))
|
||||||
|
- Remove legacy unzip code causing issues with single file zip files ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/522))
|
||||||
### Tweaks
|
### Tweaks
|
||||||
- Make server directories non-configurable ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/511))
|
- Make server directories non-configurable ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/511))
|
||||||
- Add popover to server port to detail it's purpose ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/514))
|
- Add popover to server port to detail it's purpose ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/514))
|
||||||
|
- Add server start timeout w/ WS Warning ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/518))
|
||||||
|
- Replace google ping for ntp for internet checks in locked-down countries ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/524))
|
||||||
|
- Add pushing to DockerHub registry (`arcadiatechnology/crafty-4`) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/526))
|
||||||
### Lang
|
### Lang
|
||||||
TBD
|
- Added Czech translation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/519))
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
## --- [4.0.17/4.0.18] - 2022/11/30
|
## --- [4.0.17/4.0.18] - 2022/11/30
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
*Don't Panic!*<br><br>
|
*Don't Panic!*<br><br>
|
||||||
|
|
||||||
First off, thank you for choosing Crafty Controller! <br>
|
First off, thank you for choosing Crafty Controller! <br>
|
||||||
We hope you've been enjoying the beta so far and are absolutely thrilled that you are looking to contribute!
|
We hope you've been enjoying Crafty so far and are absolutely thrilled that you are looking to contribute!
|
||||||
|
|
||||||
The following guide will show you how to easily and safely contribute to our current workflow. There are a few components that need to be taken into account and processes that need followed before we can merge your code into our repository.
|
The following guide will show you how to easily and safely contribute to our current workflow. There are a few components that need to be taken into account and processes that need followed before we can merge your code into our repository.
|
||||||
<br><br>
|
<br><br>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
|
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
|
||||||
# Crafty Controller 4.0.19
|
# Crafty Controller 4.0.20
|
||||||
> Python based Control Panel for your Minecraft Server
|
> Python based Control Panel for your Minecraft Server
|
||||||
|
|
||||||
## What is Crafty Controller?
|
## What is Crafty Controller?
|
||||||
@ -48,7 +48,7 @@ As the Dockerfile uses the permission structure of `crafty:root` **internally**
|
|||||||
### - Using the registry image 🌎
|
### - Using the registry image 🌎
|
||||||
The provided image supports both `arm64` and `amd64` out the box, if you have issues though you can build it yourself with the `compose` file in `docker/`.
|
The provided image supports both `arm64` and `amd64` out the box, if you have issues though you can build it yourself with the `compose` file in `docker/`.
|
||||||
|
|
||||||
The image is located at: `registry.gitlab.com/crafty-controller/crafty-4:latest`
|
The image is located at: `registry.gitlab.com/crafty-controller/crafty-4:latest` or `arcadiatechnology/crafty-4`
|
||||||
| Branch | Status |
|
| Branch | Status |
|
||||||
| ----------------- | ------------------------------------------------------------------ |
|
| ----------------- | ------------------------------------------------------------------ |
|
||||||
| :latest | [![pipeline status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master) |
|
| :latest | [![pipeline status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master) |
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import queue
|
||||||
|
|
||||||
from app.classes.models.management import HelpersManagement
|
from app.classes.models.management import HelpersManagement
|
||||||
from app.classes.models.servers import HelperServers
|
from app.classes.models.servers import HelperServers
|
||||||
@ -9,6 +10,26 @@ logger = logging.getLogger(__name__)
|
|||||||
class ManagementController:
|
class ManagementController:
|
||||||
def __init__(self, management_helper):
|
def __init__(self, management_helper):
|
||||||
self.management_helper = management_helper
|
self.management_helper = management_helper
|
||||||
|
self.command_queue = queue.Queue()
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
|
# Config Methods
|
||||||
|
# **********************************************************************************
|
||||||
|
@staticmethod
|
||||||
|
def set_login_image(path):
|
||||||
|
HelpersManagement.set_login_image(path)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_login_image():
|
||||||
|
return HelpersManagement.get_login_image()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_login_opacity(opacity):
|
||||||
|
return HelpersManagement.set_login_opacity(opacity)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_login_opacity():
|
||||||
|
return HelpersManagement.get_login_opacity()
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Host_Stats Methods
|
# Host_Stats Methods
|
||||||
@ -28,9 +49,6 @@ class ManagementController:
|
|||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Commands Methods
|
# Commands Methods
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
@staticmethod
|
|
||||||
def get_unactioned_commands():
|
|
||||||
return HelpersManagement.get_unactioned_commands()
|
|
||||||
|
|
||||||
def send_command(self, user_id, server_id, remote_ip, command):
|
def send_command(self, user_id, server_id, remote_ip, command):
|
||||||
server_name = HelperServers.get_server_friendly_name(server_id)
|
server_name = HelperServers.get_server_friendly_name(server_id)
|
||||||
@ -42,11 +60,12 @@ class ManagementController:
|
|||||||
server_id,
|
server_id,
|
||||||
remote_ip,
|
remote_ip,
|
||||||
)
|
)
|
||||||
HelpersManagement.add_command(server_id, user_id, remote_ip, command)
|
self.queue_command(
|
||||||
|
{"server_id": server_id, "user_id": user_id, "command": command}
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
def queue_command(self, command_data):
|
||||||
def mark_command_complete(command_id=None):
|
self.command_queue.put(command_data)
|
||||||
return HelpersManagement.mark_command_complete(command_id)
|
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Audit_Log Methods
|
# Audit_Log Methods
|
||||||
@ -78,6 +97,10 @@ class ManagementController:
|
|||||||
command,
|
command,
|
||||||
name,
|
name,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
|
one_time=False,
|
||||||
|
cron_string="* * * * *",
|
||||||
|
parent=None,
|
||||||
|
delay=0,
|
||||||
):
|
):
|
||||||
return HelpersManagement.create_scheduled_task(
|
return HelpersManagement.create_scheduled_task(
|
||||||
server_id,
|
server_id,
|
||||||
@ -88,20 +111,16 @@ class ManagementController:
|
|||||||
command,
|
command,
|
||||||
name,
|
name,
|
||||||
enabled,
|
enabled,
|
||||||
|
one_time,
|
||||||
|
cron_string,
|
||||||
|
parent,
|
||||||
|
delay,
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_scheduled_task(schedule_id):
|
def delete_scheduled_task(schedule_id):
|
||||||
return HelpersManagement.delete_scheduled_task(schedule_id)
|
return HelpersManagement.delete_scheduled_task(schedule_id)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def set_login_image(path):
|
|
||||||
HelpersManagement.set_login_image(path)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_login_image():
|
|
||||||
return HelpersManagement.get_login_image()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_scheduled_task(schedule_id, updates):
|
def update_scheduled_task(schedule_id, updates):
|
||||||
return HelpersManagement.update_scheduled_task(schedule_id, updates)
|
return HelpersManagement.update_scheduled_task(schedule_id, updates)
|
||||||
@ -145,9 +164,18 @@ class ManagementController:
|
|||||||
excluded_dirs: list = None,
|
excluded_dirs: list = None,
|
||||||
compress: bool = False,
|
compress: bool = False,
|
||||||
shutdown: bool = False,
|
shutdown: bool = False,
|
||||||
|
before: str = "",
|
||||||
|
after: str = "",
|
||||||
):
|
):
|
||||||
return self.management_helper.set_backup_config(
|
return self.management_helper.set_backup_config(
|
||||||
server_id, backup_path, max_backups, excluded_dirs, compress, shutdown
|
server_id,
|
||||||
|
backup_path,
|
||||||
|
max_backups,
|
||||||
|
excluded_dirs,
|
||||||
|
compress,
|
||||||
|
shutdown,
|
||||||
|
before,
|
||||||
|
after,
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -192,7 +192,7 @@ class ServerJars:
|
|||||||
with open(path, "wb") as output:
|
with open(path, "wb") as output:
|
||||||
shutil.copyfileobj(r.raw, output)
|
shutil.copyfileobj(r.raw, output)
|
||||||
# If this is the newer forge version we will run the installer
|
# If this is the newer forge version we will run the installer
|
||||||
if server == "forge" and int(version.split(".")[1]) > 15:
|
if server == "forge":
|
||||||
ServersController.finish_import(server_id, True)
|
ServersController.finish_import(server_id, True)
|
||||||
else:
|
else:
|
||||||
ServersController.finish_import(server_id)
|
ServersController.finish_import(server_id)
|
||||||
|
@ -300,7 +300,7 @@ class Stats:
|
|||||||
server_icon = base64.encodebytes(ping_obj["icon"])
|
server_icon = base64.encodebytes(ping_obj["icon"])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
server_icon = False
|
server_icon = False
|
||||||
logger.info(
|
logger.debug(
|
||||||
"Unable to read the server icon due to the following error:", exc_info=e
|
"Unable to read the server icon due to the following error:", exc_info=e
|
||||||
)
|
)
|
||||||
ping_data = {
|
ping_data = {
|
||||||
|
@ -13,7 +13,7 @@ from peewee import (
|
|||||||
from playhouse.shortcuts import model_to_dict
|
from playhouse.shortcuts import model_to_dict
|
||||||
|
|
||||||
from app.classes.models.base_model import BaseModel
|
from app.classes.models.base_model import BaseModel
|
||||||
from app.classes.models.users import Users, HelperUsers
|
from app.classes.models.users import HelperUsers
|
||||||
from app.classes.models.servers import Servers
|
from app.classes.models.servers import Servers
|
||||||
from app.classes.models.server_permissions import PermissionsServers
|
from app.classes.models.server_permissions import PermissionsServers
|
||||||
from app.classes.shared.main_models import DatabaseShortcuts
|
from app.classes.shared.main_models import DatabaseShortcuts
|
||||||
@ -44,6 +44,7 @@ class AuditLog(BaseModel):
|
|||||||
class CraftySettings(BaseModel):
|
class CraftySettings(BaseModel):
|
||||||
secret_api_key = CharField(default="")
|
secret_api_key = CharField(default="")
|
||||||
login_photo = CharField(default="login_1.jpg")
|
login_photo = CharField(default="login_1.jpg")
|
||||||
|
login_opacity = IntegerField(default=100)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = "crafty_settings"
|
table_name = "crafty_settings"
|
||||||
@ -68,22 +69,6 @@ class HostStats(BaseModel):
|
|||||||
table_name = "host_stats"
|
table_name = "host_stats"
|
||||||
|
|
||||||
|
|
||||||
# **********************************************************************************
|
|
||||||
# Commands Class
|
|
||||||
# **********************************************************************************
|
|
||||||
class Commands(BaseModel):
|
|
||||||
command_id = AutoField()
|
|
||||||
created = DateTimeField(default=datetime.datetime.now)
|
|
||||||
server_id = ForeignKeyField(Servers, backref="server", index=True)
|
|
||||||
user = ForeignKeyField(Users, backref="user", index=True)
|
|
||||||
source_ip = CharField(default="127.0.0.1")
|
|
||||||
command = CharField(default="")
|
|
||||||
executed = BooleanField(default=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
table_name = "commands"
|
|
||||||
|
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Webhooks Class
|
# Webhooks Class
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
@ -131,6 +116,8 @@ class Backups(BaseModel):
|
|||||||
server_id = ForeignKeyField(Servers, backref="backups_server")
|
server_id = ForeignKeyField(Servers, backref="backups_server")
|
||||||
compress = BooleanField(default=False)
|
compress = BooleanField(default=False)
|
||||||
shutdown = BooleanField(default=False)
|
shutdown = BooleanField(default=False)
|
||||||
|
before = CharField(default="")
|
||||||
|
after = CharField(default="")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = "backups"
|
table_name = "backups"
|
||||||
@ -150,33 +137,6 @@ class HelpersManagement:
|
|||||||
query = HostStats.select().order_by(HostStats.id.desc()).get()
|
query = HostStats.select().order_by(HostStats.id.desc()).get()
|
||||||
return model_to_dict(query)
|
return model_to_dict(query)
|
||||||
|
|
||||||
# **********************************************************************************
|
|
||||||
# Commands Methods
|
|
||||||
# **********************************************************************************
|
|
||||||
@staticmethod
|
|
||||||
def add_command(server_id, user_id, remote_ip, command):
|
|
||||||
Commands.insert(
|
|
||||||
{
|
|
||||||
Commands.server_id: server_id,
|
|
||||||
Commands.user: user_id,
|
|
||||||
Commands.source_ip: remote_ip,
|
|
||||||
Commands.command: command,
|
|
||||||
}
|
|
||||||
).execute()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_unactioned_commands():
|
|
||||||
query = Commands.select().where(Commands.executed == 0)
|
|
||||||
return query
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def mark_command_complete(command_id=None):
|
|
||||||
if command_id is not None:
|
|
||||||
logger.debug(f"Marking Command {command_id} completed")
|
|
||||||
Commands.update({Commands.executed: True}).where(
|
|
||||||
Commands.command_id == command_id
|
|
||||||
).execute()
|
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Audit_Log Methods
|
# Audit_Log Methods
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
@ -255,6 +215,9 @@ class HelpersManagement:
|
|||||||
)
|
)
|
||||||
return settings[0].secret_api_key
|
return settings[0].secret_api_key
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
|
# Config Methods
|
||||||
|
# **********************************************************************************
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_login_image():
|
def get_login_image():
|
||||||
settings = CraftySettings.select(CraftySettings.login_photo).where(
|
settings = CraftySettings.select(CraftySettings.login_photo).where(
|
||||||
@ -268,6 +231,19 @@ class HelpersManagement:
|
|||||||
CraftySettings.id == 1
|
CraftySettings.id == 1
|
||||||
).execute()
|
).execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_login_opacity():
|
||||||
|
settings = CraftySettings.select(CraftySettings.login_opacity).where(
|
||||||
|
CraftySettings.id == 1
|
||||||
|
)
|
||||||
|
return settings[0].login_opacity
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_login_opacity(opacity):
|
||||||
|
CraftySettings.update({CraftySettings.login_opacity: opacity}).where(
|
||||||
|
CraftySettings.id == 1
|
||||||
|
).execute()
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Schedules Methods
|
# Schedules Methods
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
@ -369,6 +345,8 @@ class HelpersManagement:
|
|||||||
"server_id": row.server_id_id,
|
"server_id": row.server_id_id,
|
||||||
"compress": row.compress,
|
"compress": row.compress,
|
||||||
"shutdown": row.shutdown,
|
"shutdown": row.shutdown,
|
||||||
|
"before": row.before,
|
||||||
|
"after": row.after,
|
||||||
}
|
}
|
||||||
except IndexError:
|
except IndexError:
|
||||||
conf = {
|
conf = {
|
||||||
@ -378,6 +356,8 @@ class HelpersManagement:
|
|||||||
"server_id": server_id,
|
"server_id": server_id,
|
||||||
"compress": False,
|
"compress": False,
|
||||||
"shutdown": False,
|
"shutdown": False,
|
||||||
|
"before": "",
|
||||||
|
"after": "",
|
||||||
}
|
}
|
||||||
return conf
|
return conf
|
||||||
|
|
||||||
@ -393,6 +373,8 @@ class HelpersManagement:
|
|||||||
excluded_dirs: list = None,
|
excluded_dirs: list = None,
|
||||||
compress: bool = False,
|
compress: bool = False,
|
||||||
shutdown: bool = False,
|
shutdown: bool = False,
|
||||||
|
before: str = "",
|
||||||
|
after: str = "",
|
||||||
):
|
):
|
||||||
logger.debug(f"Updating server {server_id} backup config with {locals()}")
|
logger.debug(f"Updating server {server_id} backup config with {locals()}")
|
||||||
if Backups.select().where(Backups.server_id == server_id).exists():
|
if Backups.select().where(Backups.server_id == server_id).exists():
|
||||||
@ -405,6 +387,8 @@ class HelpersManagement:
|
|||||||
"server_id": server_id,
|
"server_id": server_id,
|
||||||
"compress": False,
|
"compress": False,
|
||||||
"shutdown": False,
|
"shutdown": False,
|
||||||
|
"before": "",
|
||||||
|
"after": "",
|
||||||
}
|
}
|
||||||
new_row = True
|
new_row = True
|
||||||
if max_backups is not None:
|
if max_backups is not None:
|
||||||
@ -414,6 +398,8 @@ class HelpersManagement:
|
|||||||
conf["excluded_dirs"] = dirs_to_exclude
|
conf["excluded_dirs"] = dirs_to_exclude
|
||||||
conf["compress"] = compress
|
conf["compress"] = compress
|
||||||
conf["shutdown"] = shutdown
|
conf["shutdown"] = shutdown
|
||||||
|
conf["before"] = before
|
||||||
|
conf["after"] = after
|
||||||
if not new_row:
|
if not new_row:
|
||||||
with self.database.atomic():
|
with self.database.atomic():
|
||||||
if backup_path is not None:
|
if backup_path is not None:
|
||||||
@ -473,9 +459,3 @@ class HelpersManagement:
|
|||||||
f"Not removing {dir_to_del} from excluded directories - "
|
f"Not removing {dir_to_del} from excluded directories - "
|
||||||
f"not in the excluded directory list for server ID {server_id}"
|
f"not in the excluded directory list for server ID {server_id}"
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def clear_unexecuted_commands():
|
|
||||||
Commands.update({Commands.executed: True}).where(
|
|
||||||
Commands.executed == False # pylint: disable=singleton-comparison
|
|
||||||
).execute()
|
|
||||||
|
@ -298,14 +298,7 @@ class FileHelpers:
|
|||||||
try:
|
try:
|
||||||
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
||||||
zip_ref.extractall(temp_dir)
|
zip_ref.extractall(temp_dir)
|
||||||
for i in enumerate(zip_ref.filelist):
|
full_root_path = temp_dir
|
||||||
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):
|
for item in os.listdir(full_root_path):
|
||||||
if os.path.isdir(os.path.join(full_root_path, item)):
|
if os.path.isdir(os.path.join(full_root_path, item)):
|
||||||
try:
|
try:
|
||||||
|
@ -78,6 +78,7 @@ class Helpers:
|
|||||||
self.websocket_helper = WebSocketHelper(self)
|
self.websocket_helper = WebSocketHelper(self)
|
||||||
self.translation = Translation(self)
|
self.translation = Translation(self)
|
||||||
self.update_available = False
|
self.update_available = False
|
||||||
|
self.ignored_names = ["crafty_managed.txt", "db_stats"]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def auto_installer_fix(ex):
|
def auto_installer_fix(ex):
|
||||||
@ -272,7 +273,7 @@ class Helpers:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def check_internet():
|
def check_internet():
|
||||||
try:
|
try:
|
||||||
requests.get("https://google.com", timeout=1)
|
requests.get("https://ntp.org", timeout=1)
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
@ -1006,8 +1007,7 @@ class Helpers:
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@staticmethod
|
def generate_tree(self, folder, output=""):
|
||||||
def generate_tree(folder, output=""):
|
|
||||||
dir_list = []
|
dir_list = []
|
||||||
unsorted_files = []
|
unsorted_files = []
|
||||||
file_list = os.listdir(folder)
|
file_list = os.listdir(folder)
|
||||||
@ -1024,17 +1024,18 @@ class Helpers:
|
|||||||
rel = os.path.join(folder, raw_filename)
|
rel = os.path.join(folder, raw_filename)
|
||||||
dpath = os.path.join(folder, filename)
|
dpath = os.path.join(folder, filename)
|
||||||
if os.path.isdir(rel):
|
if os.path.isdir(rel):
|
||||||
output += f"""<li class="tree-item" data-path="{dpath}">
|
if filename not in self.ignored_names:
|
||||||
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
|
output += f"""<li class="tree-item" data-path="{dpath}">
|
||||||
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
|
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
|
||||||
<i style="color: var(--info);" class="far fa-folder"></i>
|
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
|
||||||
<i style="color: var(--info);" class="far fa-folder-open"></i>
|
<i style="color: var(--info);" class="far fa-folder"></i>
|
||||||
{filename}
|
<i style="color: var(--info);" class="far fa-folder-open"></i>
|
||||||
</span>
|
{filename}
|
||||||
</div><li>
|
</span>
|
||||||
\n"""
|
</div><li>
|
||||||
|
\n"""
|
||||||
else:
|
else:
|
||||||
if filename != "crafty_managed.txt":
|
if filename not in self.ignored_names:
|
||||||
output += f"""<li
|
output += f"""<li
|
||||||
class="d-block tree-ctx-item tree-file tree-item"
|
class="d-block tree-ctx-item tree-file tree-item"
|
||||||
data-path="{dpath}"
|
data-path="{dpath}"
|
||||||
@ -1043,8 +1044,7 @@ class Helpers:
|
|||||||
<i class="far fa-file"></i></span>{filename}</li>"""
|
<i class="far fa-file"></i></span>{filename}</li>"""
|
||||||
return output
|
return output
|
||||||
|
|
||||||
@staticmethod
|
def generate_dir(self, folder, output=""):
|
||||||
def generate_dir(folder, output=""):
|
|
||||||
|
|
||||||
dir_list = []
|
dir_list = []
|
||||||
unsorted_files = []
|
unsorted_files = []
|
||||||
@ -1063,16 +1063,17 @@ class Helpers:
|
|||||||
dpath = os.path.join(folder, filename)
|
dpath = os.path.join(folder, filename)
|
||||||
rel = os.path.join(folder, raw_filename)
|
rel = os.path.join(folder, raw_filename)
|
||||||
if os.path.isdir(rel):
|
if os.path.isdir(rel):
|
||||||
output += f"""<li class="tree-item" data-path="{dpath}">
|
if filename not in self.ignored_names:
|
||||||
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
|
output += f"""<li class="tree-item" data-path="{dpath}">
|
||||||
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
|
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
|
||||||
<i style="color: var(--info);" class="far fa-folder"></i>
|
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
|
||||||
<i style="color: var(--info);" class="far fa-folder-open"></i>
|
<i style="color: var(--info);" class="far fa-folder"></i>
|
||||||
{filename}
|
<i style="color: var(--info);" class="far fa-folder-open"></i>
|
||||||
</span>
|
{filename}
|
||||||
</div><li>"""
|
</span>
|
||||||
|
</div><li>"""
|
||||||
else:
|
else:
|
||||||
if filename != "crafty_managed.txt":
|
if filename not in self.ignored_names:
|
||||||
output += f"""<li
|
output += f"""<li
|
||||||
class="d-block tree-ctx-item tree-file tree-item"
|
class="d-block tree-ctx-item tree-file tree-item"
|
||||||
data-path="{dpath}"
|
data-path="{dpath}"
|
||||||
|
@ -843,6 +843,7 @@ class Controller:
|
|||||||
user_id,
|
user_id,
|
||||||
server_type="minecraft-bedrock",
|
server_type="minecraft-bedrock",
|
||||||
)
|
)
|
||||||
|
ServersController.set_import(new_id)
|
||||||
self.import_helper.import_bedrock_zip_server(
|
self.import_helper.import_bedrock_zip_server(
|
||||||
temp_dir, new_server_dir, full_jar_path, port, new_id
|
temp_dir, new_server_dir, full_jar_path, port, new_id
|
||||||
)
|
)
|
||||||
@ -992,10 +993,6 @@ class Controller:
|
|||||||
# remove the server from the DB
|
# remove the server from the DB
|
||||||
self.servers.remove_server(server_id)
|
self.servers.remove_server(server_id)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def clear_unexecuted_commands():
|
|
||||||
HelpersManagement.clear_unexecuted_commands()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def clear_support_status():
|
def clear_support_status():
|
||||||
HelperUsers.clear_support_status()
|
HelperUsers.clear_support_status()
|
||||||
|
@ -10,6 +10,7 @@ import logging.config
|
|||||||
import subprocess
|
import subprocess
|
||||||
import html
|
import html
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
import glob
|
||||||
|
|
||||||
# TZLocal is set as a hidden import on win pipeline
|
# TZLocal is set as a hidden import on win pipeline
|
||||||
from tzlocal import get_localzone
|
from tzlocal import get_localzone
|
||||||
@ -130,13 +131,13 @@ class ServerInstance:
|
|||||||
self.stats_helper = HelperServerStats(self.server_id)
|
self.stats_helper = HelperServerStats(self.server_id)
|
||||||
self.last_backup_failed = False
|
self.last_backup_failed = False
|
||||||
try:
|
try:
|
||||||
tz = get_localzone()
|
self.tz = get_localzone()
|
||||||
except ZoneInfoNotFoundError:
|
except ZoneInfoNotFoundError:
|
||||||
logger.error(
|
logger.error(
|
||||||
"Could not capture time zone from system. Falling back to Europe/London"
|
"Could not capture time zone from system. Falling back to Europe/London"
|
||||||
)
|
)
|
||||||
tz = "Europe/London"
|
self.tz = "Europe/London"
|
||||||
self.server_scheduler = BackgroundScheduler(timezone=str(tz))
|
self.server_scheduler = BackgroundScheduler(timezone=str(self.tz))
|
||||||
self.server_scheduler.start()
|
self.server_scheduler.start()
|
||||||
self.backup_thread = threading.Thread(
|
self.backup_thread = threading.Thread(
|
||||||
target=self.a_backup_server, daemon=True, name=f"backup_{self.name}"
|
target=self.a_backup_server, daemon=True, name=f"backup_{self.name}"
|
||||||
@ -174,7 +175,6 @@ class ServerInstance:
|
|||||||
self.name = server_name
|
self.name = server_name
|
||||||
self.settings = server_data_obj
|
self.settings = server_data_obj
|
||||||
|
|
||||||
self.stats_helper.init_database(server_id)
|
|
||||||
self.record_server_stats()
|
self.record_server_stats()
|
||||||
|
|
||||||
# build our server run command
|
# build our server run command
|
||||||
@ -580,54 +580,105 @@ class ServerInstance:
|
|||||||
# Process has exited. Lets do some work to setup the new
|
# Process has exited. Lets do some work to setup the new
|
||||||
# run command.
|
# run command.
|
||||||
# Let's grab the server object we're going to update.
|
# Let's grab the server object we're going to update.
|
||||||
server_obj = HelperServers.get_server_obj(self.server_id)
|
server_obj: Servers = HelperServers.get_server_obj(self.server_id)
|
||||||
|
|
||||||
# The forge install is done so we can delete that install file.
|
# The forge install is done so we can delete that install file.
|
||||||
os.remove(os.path.join(server_obj.path, server_obj.executable))
|
os.remove(os.path.join(server_obj.path, server_obj.executable))
|
||||||
|
|
||||||
# We need to grab the exact forge version number.
|
# We need to grab the exact forge version number.
|
||||||
# We know we can find it here in the run.sh/bat script.
|
# We know we can find it here in the run.sh/bat script.
|
||||||
run_file_path = ""
|
try:
|
||||||
if self.helper.is_os_windows():
|
|
||||||
run_file_path = os.path.join(server_obj.path, "run.bat")
|
|
||||||
else:
|
|
||||||
run_file_path = os.path.join(server_obj.path, "run.sh")
|
|
||||||
|
|
||||||
if Helpers.check_file_perms(run_file_path) and os.path.isfile(
|
# Getting the forge version from the executable command
|
||||||
run_file_path
|
version = re.findall(
|
||||||
):
|
r"forge-([0-9\.]+)((?:)|(?:-([0-9\.]+)-[a-zA-Z]+)).jar",
|
||||||
run_file = open(run_file_path, "r", encoding="utf-8")
|
server_obj.execution_command,
|
||||||
run_file_text = run_file.read()
|
|
||||||
else:
|
|
||||||
Console.error(
|
|
||||||
"ERROR ! Forge install can't read the scripts files."
|
|
||||||
" Aborting ..."
|
|
||||||
)
|
)
|
||||||
return
|
version_param = version[0][0].split(".")
|
||||||
|
version_major = int(version_param[0])
|
||||||
|
version_minor = int(version_param[1])
|
||||||
|
|
||||||
# We get the server command parameters from forge script
|
# Checking which version we are with
|
||||||
server_command = re.findall(
|
if version_major <= 1 and version_minor < 17:
|
||||||
r"java @([a-zA-Z0-9_\.]+)"
|
# OLD VERSION < 1.17
|
||||||
r" @([a-z.\/\-]+)([0-9.\-]+)\/\b([a-z_0-9]+\.txt)\b( .{2,4})?",
|
|
||||||
run_file_text,
|
|
||||||
)[0]
|
|
||||||
|
|
||||||
version = server_command[2]
|
# Retrieving the executable jar filename
|
||||||
executable_path = f"{server_command[1]}{server_command[2]}/"
|
file_path = glob.glob(
|
||||||
|
f"{server_obj.path}/forge-{version[0][0]}*.jar"
|
||||||
|
)[0]
|
||||||
|
file_name = re.findall(
|
||||||
|
r"(forge[-0-9.]+.jar)",
|
||||||
|
file_path,
|
||||||
|
)[0]
|
||||||
|
|
||||||
# Let's set the proper server executable
|
# Let's set the proper server executable
|
||||||
server_obj.executable = os.path.join(
|
server_obj.executable = os.path.join(file_name)
|
||||||
f"{executable_path}forge-{version}-server.jar"
|
|
||||||
)
|
# Get memory values
|
||||||
# Now lets set up the new run command.
|
memory_values = re.findall(
|
||||||
# This is based off the run.sh/bat that
|
r"-Xms([A-Z0-9\.]+) -Xmx([A-Z0-9\.]+)",
|
||||||
# Forge uses in 1.16 and <
|
server_obj.execution_command,
|
||||||
execution_command = (
|
)
|
||||||
f"java @{server_command[0]}"
|
|
||||||
f" @{executable_path}{server_command[3]} nogui {server_command[4]}"
|
# Now lets set up the new run command.
|
||||||
)
|
# This is based off the run.sh/bat that
|
||||||
server_obj.execution_command = execution_command
|
# Forge uses in 1.17 and <
|
||||||
Console.debug("SUCCESS! Forge install completed")
|
execution_command = (
|
||||||
|
f"java -Xms{memory_values[0][0]} -Xmx{memory_values[0][1]}"
|
||||||
|
f' -jar "{file_name}" nogui'
|
||||||
|
)
|
||||||
|
server_obj.execution_command = execution_command
|
||||||
|
Console.debug("SUCCESS! Forge install completed")
|
||||||
|
|
||||||
|
else:
|
||||||
|
# NEW VERSION >= 1.17
|
||||||
|
|
||||||
|
run_file_path = ""
|
||||||
|
if self.helper.is_os_windows():
|
||||||
|
run_file_path = os.path.join(server_obj.path, "run.bat")
|
||||||
|
else:
|
||||||
|
run_file_path = os.path.join(server_obj.path, "run.sh")
|
||||||
|
|
||||||
|
if Helpers.check_file_perms(run_file_path) and os.path.isfile(
|
||||||
|
run_file_path
|
||||||
|
):
|
||||||
|
run_file = open(run_file_path, "r", encoding="utf-8")
|
||||||
|
run_file_text = run_file.read()
|
||||||
|
else:
|
||||||
|
Console.error(
|
||||||
|
"ERROR ! Forge install can't read the scripts files."
|
||||||
|
" Aborting ..."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# We get the server command parameters from forge script
|
||||||
|
server_command = re.findall(
|
||||||
|
r"java @([a-zA-Z0-9_\.]+)"
|
||||||
|
r" @([a-z.\/\-]+)([0-9.\-]+)"
|
||||||
|
r"\/\b([a-z_0-9]+\.txt)\b( .{2,4})?",
|
||||||
|
run_file_text,
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
version = server_command[2]
|
||||||
|
executable_path = f"{server_command[1]}{server_command[2]}/"
|
||||||
|
|
||||||
|
# Let's set the proper server executable
|
||||||
|
server_obj.executable = os.path.join(
|
||||||
|
f"{executable_path}forge-{version}-server.jar"
|
||||||
|
)
|
||||||
|
# Now lets set up the new run command.
|
||||||
|
# This is based off the run.sh/bat that
|
||||||
|
# Forge uses in 1.17 and <
|
||||||
|
execution_command = (
|
||||||
|
f"java @{server_command[0]}"
|
||||||
|
f" @{executable_path}{server_command[3]} nogui"
|
||||||
|
" {server_command[4]}"
|
||||||
|
)
|
||||||
|
server_obj.execution_command = execution_command
|
||||||
|
Console.debug("SUCCESS! Forge install completed")
|
||||||
|
except:
|
||||||
|
logger.debug("Could not find run file.")
|
||||||
|
# TODO Use regex to get version and rebuild simple execution
|
||||||
|
|
||||||
# We'll update the server with the new information now.
|
# We'll update the server with the new information now.
|
||||||
HelperServers.update_server(server_obj)
|
HelperServers.update_server(server_obj)
|
||||||
@ -972,7 +1023,17 @@ class ServerInstance:
|
|||||||
)
|
)
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
conf = HelpersManagement.get_backup_config(self.server_id)
|
conf = HelpersManagement.get_backup_config(self.server_id)
|
||||||
|
if conf["before"]:
|
||||||
|
if self.check_running():
|
||||||
|
logger.debug(
|
||||||
|
"Found running server and send command option. Sending command"
|
||||||
|
)
|
||||||
|
self.send_command(conf["before"])
|
||||||
|
|
||||||
if conf["shutdown"]:
|
if conf["shutdown"]:
|
||||||
|
if conf["before"]:
|
||||||
|
# pause to let people read message.
|
||||||
|
time.sleep(5)
|
||||||
logger.info(
|
logger.info(
|
||||||
"Found shutdown preference. Delaying"
|
"Found shutdown preference. Delaying"
|
||||||
+ "backup start. Shutting down server."
|
+ "backup start. Shutting down server."
|
||||||
@ -985,7 +1046,7 @@ class ServerInstance:
|
|||||||
try:
|
try:
|
||||||
backup_filename = (
|
backup_filename = (
|
||||||
f"{self.settings['backup_path']}/"
|
f"{self.settings['backup_path']}/"
|
||||||
f"{datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}"
|
f"{datetime.datetime.now().astimezone(self.tz).strftime('%Y-%m-%d_%H-%M-%S')}" # pylint: disable=line-too-long
|
||||||
)
|
)
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Creating backup of server '{self.settings['server_name']}'"
|
f"Creating backup of server '{self.settings['server_name']}'"
|
||||||
@ -1053,6 +1114,14 @@ class ServerInstance:
|
|||||||
self.run_threaded_server(HelperUsers.get_user_id_by_name("system"))
|
self.run_threaded_server(HelperUsers.get_user_id_by_name("system"))
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
self.last_backup_failed = False
|
self.last_backup_failed = False
|
||||||
|
if conf["after"]:
|
||||||
|
if self.check_running():
|
||||||
|
logger.debug(
|
||||||
|
"Found running server and send command option. Sending command"
|
||||||
|
)
|
||||||
|
self.send_command(conf["after"])
|
||||||
|
# pause to let people read message.
|
||||||
|
time.sleep(5)
|
||||||
except:
|
except:
|
||||||
logger.exception(
|
logger.exception(
|
||||||
f"Failed to create backup of server {self.name} (ID {self.server_id})"
|
f"Failed to create backup of server {self.name} (ID {self.server_id})"
|
||||||
|
@ -91,22 +91,21 @@ class TasksManager:
|
|||||||
def command_watcher(self):
|
def command_watcher(self):
|
||||||
while True:
|
while True:
|
||||||
# select any commands waiting to be processed
|
# select any commands waiting to be processed
|
||||||
commands = HelpersManagement.get_unactioned_commands()
|
if not self.controller.management.command_queue.empty():
|
||||||
for cmd in commands:
|
cmd = self.controller.management.command_queue.get()
|
||||||
try:
|
try:
|
||||||
svr = self.controller.servers.get_server_instance_by_id(
|
svr = self.controller.servers.get_server_instance_by_id(
|
||||||
cmd.server_id.server_id
|
cmd["server_id"]
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
logger.error(
|
logger.error(
|
||||||
"Server value requested does not exist! "
|
"Server value requested does not exist! "
|
||||||
"Purging item from waiting commands."
|
"Purging item from waiting commands."
|
||||||
)
|
)
|
||||||
HelpersManagement.mark_command_complete(cmd.command_id)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
user_id = cmd.user_id
|
user_id = cmd["user_id"]
|
||||||
command = cmd.command
|
command = cmd["command"]
|
||||||
|
|
||||||
if command == "start_server":
|
if command == "start_server":
|
||||||
svr.run_threaded_server(user_id)
|
svr.run_threaded_server(user_id)
|
||||||
@ -136,8 +135,6 @@ class TasksManager:
|
|||||||
else:
|
else:
|
||||||
svr.send_command(command)
|
svr.send_command(command)
|
||||||
|
|
||||||
HelpersManagement.mark_command_complete(cmd.command_id)
|
|
||||||
|
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
def _main_graceful_exit(self):
|
def _main_graceful_exit(self):
|
||||||
@ -212,16 +209,19 @@ class TasksManager:
|
|||||||
if schedule.cron_string != "":
|
if schedule.cron_string != "":
|
||||||
try:
|
try:
|
||||||
new_job = self.scheduler.add_job(
|
new_job = self.scheduler.add_job(
|
||||||
HelpersManagement.add_command,
|
self.controller.management.queue_command,
|
||||||
CronTrigger.from_crontab(
|
CronTrigger.from_crontab(
|
||||||
schedule.cron_string, timezone=str(self.tz)
|
schedule.cron_string, timezone=str(self.tz)
|
||||||
),
|
),
|
||||||
id=str(schedule.schedule_id),
|
id=str(schedule.schedule_id),
|
||||||
args=[
|
args=[
|
||||||
schedule.server_id,
|
{
|
||||||
self.users_controller.get_id_by_name("system"),
|
"server_id": schedule.server_id.server_id,
|
||||||
"127.0.0.1",
|
"user_id": self.users_controller.get_id_by_name(
|
||||||
schedule.command,
|
"system"
|
||||||
|
),
|
||||||
|
"command": schedule.command,
|
||||||
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -237,45 +237,54 @@ class TasksManager:
|
|||||||
else:
|
else:
|
||||||
if schedule.interval_type == "hours":
|
if schedule.interval_type == "hours":
|
||||||
new_job = self.scheduler.add_job(
|
new_job = self.scheduler.add_job(
|
||||||
HelpersManagement.add_command,
|
self.controller.management.queue_command,
|
||||||
"cron",
|
"cron",
|
||||||
minute=0,
|
minute=0,
|
||||||
hour="*/" + str(schedule.interval),
|
hour="*/" + str(schedule.interval),
|
||||||
id=str(schedule.schedule_id),
|
id=str(schedule.schedule_id),
|
||||||
args=[
|
args=[
|
||||||
schedule.server_id,
|
{
|
||||||
self.users_controller.get_id_by_name("system"),
|
"server_id": schedule.server_id.server_id,
|
||||||
"127.0.0.1",
|
"user_id": self.users_controller.get_id_by_name(
|
||||||
schedule.command,
|
"system"
|
||||||
|
),
|
||||||
|
"command": schedule.command,
|
||||||
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
elif schedule.interval_type == "minutes":
|
elif schedule.interval_type == "minutes":
|
||||||
new_job = self.scheduler.add_job(
|
new_job = self.scheduler.add_job(
|
||||||
HelpersManagement.add_command,
|
self.controller.management.queue_command,
|
||||||
"cron",
|
"cron",
|
||||||
minute="*/" + str(schedule.interval),
|
minute="*/" + str(schedule.interval),
|
||||||
id=str(schedule.schedule_id),
|
id=str(schedule.schedule_id),
|
||||||
args=[
|
args=[
|
||||||
schedule.server_id,
|
{
|
||||||
self.users_controller.get_id_by_name("system"),
|
"server_id": schedule.server_id.server_id,
|
||||||
"127.0.0.1",
|
"user_id": self.users_controller.get_id_by_name(
|
||||||
schedule.command,
|
"system"
|
||||||
|
),
|
||||||
|
"command": schedule.command,
|
||||||
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
elif schedule.interval_type == "days":
|
elif schedule.interval_type == "days":
|
||||||
curr_time = schedule.start_time.split(":")
|
curr_time = schedule.start_time.split(":")
|
||||||
new_job = self.scheduler.add_job(
|
new_job = self.scheduler.add_job(
|
||||||
HelpersManagement.add_command,
|
self.controller.management.queue_command,
|
||||||
"cron",
|
"cron",
|
||||||
day="*/" + str(schedule.interval),
|
day="*/" + str(schedule.interval),
|
||||||
hour=curr_time[0],
|
hour=curr_time[0],
|
||||||
minute=curr_time[1],
|
minute=curr_time[1],
|
||||||
id=str(schedule.schedule_id),
|
id=str(schedule.schedule_id),
|
||||||
args=[
|
args=[
|
||||||
schedule.server_id,
|
{
|
||||||
self.users_controller.get_id_by_name("system"),
|
"server_id": schedule.server_id.server_id,
|
||||||
"127.0.0.1",
|
"user_id": self.users_controller.get_id_by_name(
|
||||||
schedule.command,
|
"system"
|
||||||
|
),
|
||||||
|
"command": schedule.command,
|
||||||
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
if new_job != "error":
|
if new_job != "error":
|
||||||
@ -322,16 +331,19 @@ class TasksManager:
|
|||||||
if job_data["cron_string"] != "":
|
if job_data["cron_string"] != "":
|
||||||
try:
|
try:
|
||||||
new_job = self.scheduler.add_job(
|
new_job = self.scheduler.add_job(
|
||||||
HelpersManagement.add_command,
|
self.controller.management.queue_command,
|
||||||
CronTrigger.from_crontab(
|
CronTrigger.from_crontab(
|
||||||
job_data["cron_string"], timezone=str(self.tz)
|
job_data["cron_string"], timezone=str(self.tz)
|
||||||
),
|
),
|
||||||
id=str(sch_id),
|
id=str(sch_id),
|
||||||
args=[
|
args=[
|
||||||
job_data["server_id"],
|
{
|
||||||
self.users_controller.get_id_by_name("system"),
|
"server_id": job_data["server_id"],
|
||||||
"127.0.0.1",
|
"user_id": self.users_controller.get_id_by_name(
|
||||||
job_data["command"],
|
"system"
|
||||||
|
),
|
||||||
|
"command": job_data["command"],
|
||||||
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -345,45 +357,54 @@ class TasksManager:
|
|||||||
else:
|
else:
|
||||||
if job_data["interval_type"] == "hours":
|
if job_data["interval_type"] == "hours":
|
||||||
new_job = self.scheduler.add_job(
|
new_job = self.scheduler.add_job(
|
||||||
HelpersManagement.add_command,
|
self.controller.management.queue_command,
|
||||||
"cron",
|
"cron",
|
||||||
minute=0,
|
minute=0,
|
||||||
hour="*/" + str(job_data["interval"]),
|
hour="*/" + str(job_data["interval"]),
|
||||||
id=str(sch_id),
|
id=str(sch_id),
|
||||||
args=[
|
args=[
|
||||||
job_data["server_id"],
|
{
|
||||||
self.users_controller.get_id_by_name("system"),
|
"server_id": job_data["server_id"],
|
||||||
"127.0.0.1",
|
"user_id": self.users_controller.get_id_by_name(
|
||||||
job_data["command"],
|
"system"
|
||||||
|
),
|
||||||
|
"command": job_data["command"],
|
||||||
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
elif job_data["interval_type"] == "minutes":
|
elif job_data["interval_type"] == "minutes":
|
||||||
new_job = self.scheduler.add_job(
|
new_job = self.scheduler.add_job(
|
||||||
HelpersManagement.add_command,
|
self.controller.management.queue_command,
|
||||||
"cron",
|
"cron",
|
||||||
minute="*/" + str(job_data["interval"]),
|
minute="*/" + str(job_data["interval"]),
|
||||||
id=str(sch_id),
|
id=str(sch_id),
|
||||||
args=[
|
args=[
|
||||||
job_data["server_id"],
|
{
|
||||||
self.users_controller.get_id_by_name("system"),
|
"server_id": job_data["server_id"],
|
||||||
"127.0.0.1",
|
"user_id": self.users_controller.get_id_by_name(
|
||||||
job_data["command"],
|
"system"
|
||||||
|
),
|
||||||
|
"command": job_data["command"],
|
||||||
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
elif job_data["interval_type"] == "days":
|
elif job_data["interval_type"] == "days":
|
||||||
curr_time = job_data["start_time"].split(":")
|
curr_time = job_data["start_time"].split(":")
|
||||||
new_job = self.scheduler.add_job(
|
new_job = self.scheduler.add_job(
|
||||||
HelpersManagement.add_command,
|
self.controller.management.queue_command,
|
||||||
"cron",
|
"cron",
|
||||||
day="*/" + str(job_data["interval"]),
|
day="*/" + str(job_data["interval"]),
|
||||||
hour=curr_time[0],
|
hour=curr_time[0],
|
||||||
minute=curr_time[1],
|
minute=curr_time[1],
|
||||||
id=str(sch_id),
|
id=str(sch_id),
|
||||||
args=[
|
args=[
|
||||||
job_data["server_id"],
|
{
|
||||||
self.users_controller.get_id_by_name("system"),
|
"server_id": job_data["server_id"],
|
||||||
"127.0.0.1",
|
"user_id": self.users_controller.get_id_by_name(
|
||||||
job_data["command"],
|
"system"
|
||||||
|
),
|
||||||
|
"command": job_data["command"],
|
||||||
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
logger.info("Added job. Current enabled schedules: ")
|
logger.info("Added job. Current enabled schedules: ")
|
||||||
@ -460,16 +481,19 @@ class TasksManager:
|
|||||||
if job_data["cron_string"] != "":
|
if job_data["cron_string"] != "":
|
||||||
try:
|
try:
|
||||||
new_job = self.scheduler.add_job(
|
new_job = self.scheduler.add_job(
|
||||||
HelpersManagement.add_command,
|
self.controller.management.queue_command,
|
||||||
CronTrigger.from_crontab(
|
CronTrigger.from_crontab(
|
||||||
job_data["cron_string"], timezone=str(self.tz)
|
job_data["cron_string"], timezone=str(self.tz)
|
||||||
),
|
),
|
||||||
id=str(sch_id),
|
id=str(sch_id),
|
||||||
args=[
|
args=[
|
||||||
job_data["server_id"],
|
{
|
||||||
self.users_controller.get_id_by_name("system"),
|
"server_id": job_data["server_id"],
|
||||||
"127.0.0.1",
|
"user_id": self.users_controller.get_id_by_name(
|
||||||
job_data["command"],
|
"system"
|
||||||
|
),
|
||||||
|
"command": job_data["command"],
|
||||||
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -480,45 +504,54 @@ class TasksManager:
|
|||||||
else:
|
else:
|
||||||
if job_data["interval_type"] == "hours":
|
if job_data["interval_type"] == "hours":
|
||||||
new_job = self.scheduler.add_job(
|
new_job = self.scheduler.add_job(
|
||||||
HelpersManagement.add_command,
|
self.controller.management.queue_command,
|
||||||
"cron",
|
"cron",
|
||||||
minute=0,
|
minute=0,
|
||||||
hour="*/" + str(job_data["interval"]),
|
hour="*/" + str(job_data["interval"]),
|
||||||
id=str(sch_id),
|
id=str(sch_id),
|
||||||
args=[
|
args=[
|
||||||
job_data["server_id"],
|
{
|
||||||
self.users_controller.get_id_by_name("system"),
|
"server_id": job_data["server_id"],
|
||||||
"127.0.0.1",
|
"user_id": self.users_controller.get_id_by_name(
|
||||||
job_data["command"],
|
"system"
|
||||||
|
),
|
||||||
|
"command": job_data["command"],
|
||||||
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
elif job_data["interval_type"] == "minutes":
|
elif job_data["interval_type"] == "minutes":
|
||||||
new_job = self.scheduler.add_job(
|
new_job = self.scheduler.add_job(
|
||||||
HelpersManagement.add_command,
|
self.controller.management.queue_command,
|
||||||
"cron",
|
"cron",
|
||||||
minute="*/" + str(job_data["interval"]),
|
minute="*/" + str(job_data["interval"]),
|
||||||
id=str(sch_id),
|
id=str(sch_id),
|
||||||
args=[
|
args=[
|
||||||
job_data["server_id"],
|
{
|
||||||
self.users_controller.get_id_by_name("system"),
|
"server_id": job_data["server_id"],
|
||||||
"127.0.0.1",
|
"user_id": self.users_controller.get_id_by_name(
|
||||||
job_data["command"],
|
"system"
|
||||||
|
),
|
||||||
|
"command": job_data["command"],
|
||||||
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
elif job_data["interval_type"] == "days":
|
elif job_data["interval_type"] == "days":
|
||||||
curr_time = job_data["start_time"].split(":")
|
curr_time = job_data["start_time"].split(":")
|
||||||
new_job = self.scheduler.add_job(
|
new_job = self.scheduler.add_job(
|
||||||
HelpersManagement.add_command,
|
self.controller.management.queue_command,
|
||||||
"cron",
|
"cron",
|
||||||
day="*/" + str(job_data["interval"]),
|
day="*/" + str(job_data["interval"]),
|
||||||
hour=curr_time[0],
|
hour=curr_time[0],
|
||||||
minute=curr_time[1],
|
minute=curr_time[1],
|
||||||
id=str(sch_id),
|
id=str(sch_id),
|
||||||
args=[
|
args=[
|
||||||
job_data["server_id"],
|
{
|
||||||
self.users_controller.get_id_by_name("system"),
|
"server_id": job_data["server_id"],
|
||||||
"127.0.0.1",
|
"user_id": self.users_controller.get_id_by_name(
|
||||||
job_data["command"],
|
"system"
|
||||||
|
),
|
||||||
|
"command": job_data["command"],
|
||||||
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
if new_job != "error":
|
if new_job != "error":
|
||||||
@ -556,7 +589,7 @@ class TasksManager:
|
|||||||
if task.one_time:
|
if task.one_time:
|
||||||
self.remove_job(task.schedule_id)
|
self.remove_job(task.schedule_id)
|
||||||
logger.info("one time task detected. Deleting...")
|
logger.info("one time task detected. Deleting...")
|
||||||
else:
|
elif task.interval_type != "reaction":
|
||||||
self.controller.management.update_scheduled_task(
|
self.controller.management.update_scheduled_task(
|
||||||
task.schedule_id,
|
task.schedule_id,
|
||||||
{
|
{
|
||||||
@ -579,15 +612,18 @@ class TasksManager:
|
|||||||
seconds=schedule.delay
|
seconds=schedule.delay
|
||||||
)
|
)
|
||||||
self.scheduler.add_job(
|
self.scheduler.add_job(
|
||||||
HelpersManagement.add_command,
|
self.controller.management.queue_command,
|
||||||
"date",
|
"date",
|
||||||
run_date=delaytime,
|
run_date=delaytime,
|
||||||
id=str(schedule.schedule_id),
|
id=str(schedule.schedule_id),
|
||||||
args=[
|
args=[
|
||||||
schedule.server_id,
|
{
|
||||||
self.users_controller.get_id_by_name("system"),
|
"server_id": schedule.server_id.server_id,
|
||||||
"127.0.0.1",
|
"user_id": self.users_controller.get_id_by_name(
|
||||||
schedule.command,
|
"system"
|
||||||
|
),
|
||||||
|
"command": schedule.command,
|
||||||
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -348,14 +348,11 @@ class AjaxHandler(BaseHandler):
|
|||||||
|
|
||||||
server.backup_server()
|
server.backup_server()
|
||||||
|
|
||||||
elif page == "clear_comms":
|
|
||||||
if exec_user["superuser"]:
|
|
||||||
self.controller.clear_unexecuted_commands()
|
|
||||||
return
|
|
||||||
|
|
||||||
elif page == "select_photo":
|
elif page == "select_photo":
|
||||||
if exec_user["superuser"]:
|
if exec_user["superuser"]:
|
||||||
photo = self.get_argument("photo", None)
|
photo = urllib.parse.unquote(self.get_argument("photo", ""))
|
||||||
|
opacity = self.get_argument("opacity", 100)
|
||||||
|
self.controller.management.set_login_opacity(int(opacity))
|
||||||
if photo == "login_1.jpg":
|
if photo == "login_1.jpg":
|
||||||
self.controller.management.set_login_image("login_1.jpg")
|
self.controller.management.set_login_image("login_1.jpg")
|
||||||
self.controller.cached_login = f"{photo}"
|
self.controller.cached_login = f"{photo}"
|
||||||
@ -366,7 +363,7 @@ class AjaxHandler(BaseHandler):
|
|||||||
|
|
||||||
elif page == "delete_photo":
|
elif page == "delete_photo":
|
||||||
if exec_user["superuser"]:
|
if exec_user["superuser"]:
|
||||||
photo = self.get_argument("photo", None)
|
photo = urllib.parse.unquote(self.get_argument("photo", None))
|
||||||
if photo and photo != "login_1.jpg":
|
if photo and photo != "login_1.jpg":
|
||||||
os.remove(
|
os.remove(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
@ -440,15 +437,8 @@ class AjaxHandler(BaseHandler):
|
|||||||
for schedule in self.controller.management.get_schedules_by_server(
|
for schedule in self.controller.management.get_schedules_by_server(
|
||||||
server_id
|
server_id
|
||||||
):
|
):
|
||||||
self.controller.management.create_scheduled_task(
|
self.tasks_manager.update_job(
|
||||||
new_server_id,
|
schedule.schedule_id, {"server_id": new_server_id}
|
||||||
schedule.action,
|
|
||||||
schedule.interval,
|
|
||||||
schedule.interval_type,
|
|
||||||
schedule.start_time,
|
|
||||||
schedule.command,
|
|
||||||
schedule.name,
|
|
||||||
schedule.enabled,
|
|
||||||
)
|
)
|
||||||
# preserve execution command
|
# preserve execution command
|
||||||
new_server_obj = self.controller.servers.get_server_obj(
|
new_server_obj = self.controller.servers.get_server_obj(
|
||||||
@ -456,6 +446,29 @@ class AjaxHandler(BaseHandler):
|
|||||||
)
|
)
|
||||||
new_server_obj.execution_command = server_data["execution_command"]
|
new_server_obj.execution_command = server_data["execution_command"]
|
||||||
self.controller.servers.update_server(new_server_obj)
|
self.controller.servers.update_server(new_server_obj)
|
||||||
|
|
||||||
|
# preserve backup config
|
||||||
|
backup_config = self.controller.management.get_backup_config(
|
||||||
|
server_id
|
||||||
|
)
|
||||||
|
excluded_dirs = []
|
||||||
|
server_obj = self.controller.servers.get_server_obj(server_id)
|
||||||
|
loop_backup_path = self.helper.wtol_path(server_obj.path)
|
||||||
|
for item in self.controller.management.get_excluded_backup_dirs(
|
||||||
|
server_id
|
||||||
|
):
|
||||||
|
item_path = self.helper.wtol_path(item)
|
||||||
|
bu_path = os.path.relpath(item_path, loop_backup_path)
|
||||||
|
bu_path = os.path.join(new_server_obj.path, bu_path)
|
||||||
|
excluded_dirs.append(bu_path)
|
||||||
|
self.controller.management.set_backup_config(
|
||||||
|
new_server_id,
|
||||||
|
new_server_obj.backup_path,
|
||||||
|
backup_config["max_backups"],
|
||||||
|
excluded_dirs,
|
||||||
|
backup_config["compress"],
|
||||||
|
backup_config["shutdown"],
|
||||||
|
)
|
||||||
# remove old server's tasks
|
# remove old server's tasks
|
||||||
try:
|
try:
|
||||||
self.tasks_manager.remove_all_server_tasks(server_id)
|
self.tasks_manager.remove_all_server_tasks(server_id)
|
||||||
@ -484,15 +497,8 @@ class AjaxHandler(BaseHandler):
|
|||||||
for schedule in self.controller.management.get_schedules_by_server(
|
for schedule in self.controller.management.get_schedules_by_server(
|
||||||
server_id
|
server_id
|
||||||
):
|
):
|
||||||
self.controller.management.create_scheduled_task(
|
self.tasks_manager.update_job(
|
||||||
new_server_id,
|
schedule.schedule_id, {"server_id": new_server_id}
|
||||||
schedule.action,
|
|
||||||
schedule.interval,
|
|
||||||
schedule.interval_type,
|
|
||||||
schedule.start_time,
|
|
||||||
schedule.command,
|
|
||||||
schedule.name,
|
|
||||||
schedule.enabled,
|
|
||||||
)
|
)
|
||||||
# preserve execution command
|
# preserve execution command
|
||||||
new_server_obj = self.controller.servers.get_server_obj(
|
new_server_obj = self.controller.servers.get_server_obj(
|
||||||
@ -500,6 +506,29 @@ class AjaxHandler(BaseHandler):
|
|||||||
)
|
)
|
||||||
new_server_obj.execution_command = server_data["execution_command"]
|
new_server_obj.execution_command = server_data["execution_command"]
|
||||||
self.controller.servers.update_server(new_server_obj)
|
self.controller.servers.update_server(new_server_obj)
|
||||||
|
|
||||||
|
# preserve backup config
|
||||||
|
backup_config = self.controller.management.get_backup_config(
|
||||||
|
server_id
|
||||||
|
)
|
||||||
|
excluded_dirs = []
|
||||||
|
server_obj = self.controller.servers.get_server_obj(server_id)
|
||||||
|
loop_backup_path = self.helper.wtol_path(server_obj.path)
|
||||||
|
for item in self.controller.management.get_excluded_backup_dirs(
|
||||||
|
server_id
|
||||||
|
):
|
||||||
|
item_path = self.helper.wtol_path(item)
|
||||||
|
bu_path = os.path.relpath(item_path, loop_backup_path)
|
||||||
|
bu_path = os.path.join(new_server_obj.path, bu_path)
|
||||||
|
excluded_dirs.append(bu_path)
|
||||||
|
self.controller.management.set_backup_config(
|
||||||
|
new_server_id,
|
||||||
|
new_server_obj.backup_path,
|
||||||
|
backup_config["max_backups"],
|
||||||
|
excluded_dirs,
|
||||||
|
backup_config["compress"],
|
||||||
|
backup_config["shutdown"],
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
self.tasks_manager.remove_all_server_tasks(server_id)
|
self.tasks_manager.remove_all_server_tasks(server_id)
|
||||||
except:
|
except:
|
||||||
|
@ -17,6 +17,5 @@ class DefaultHandler(BaseHandler):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.redirect(
|
self.redirect(
|
||||||
"/public/login",
|
"/login",
|
||||||
# translate=self.translator.translate,
|
|
||||||
)
|
)
|
||||||
|
@ -100,7 +100,7 @@ class FileHandler(BaseHandler):
|
|||||||
self.write(
|
self.write(
|
||||||
Helpers.get_os_understandable_path(path)
|
Helpers.get_os_understandable_path(path)
|
||||||
+ "\n"
|
+ "\n"
|
||||||
+ Helpers.generate_tree(path)
|
+ self.helper.generate_tree(path)
|
||||||
)
|
)
|
||||||
self.finish()
|
self.finish()
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ class FileHandler(BaseHandler):
|
|||||||
self.write(
|
self.write(
|
||||||
Helpers.get_os_understandable_path(path)
|
Helpers.get_os_understandable_path(path)
|
||||||
+ "\n"
|
+ "\n"
|
||||||
+ Helpers.generate_dir(path)
|
+ self.helper.generate_dir(path)
|
||||||
)
|
)
|
||||||
self.finish()
|
self.finish()
|
||||||
|
|
||||||
|
@ -291,6 +291,7 @@ class PanelHandler(BaseHandler):
|
|||||||
# todo: make this actually pull and compare version data
|
# todo: make this actually pull and compare version data
|
||||||
"update_available": self.helper.update_available,
|
"update_available": self.helper.update_available,
|
||||||
"background": self.controller.cached_login,
|
"background": self.controller.cached_login,
|
||||||
|
"login_opacity": self.controller.management.get_login_opacity(),
|
||||||
"serverTZ": tz,
|
"serverTZ": tz,
|
||||||
"version_data": self.helper.get_version_string(),
|
"version_data": self.helper.get_version_string(),
|
||||||
"failed_servers": self.controller.servers.failed_servers,
|
"failed_servers": self.controller.servers.failed_servers,
|
||||||
@ -857,32 +858,6 @@ class PanelHandler(BaseHandler):
|
|||||||
page_data["roles"] = self.controller.roles.get_all_roles()
|
page_data["roles"] = self.controller.roles.get_all_roles()
|
||||||
page_data["auth-servers"][user.user_id] = super_auth_servers
|
page_data["auth-servers"][user.user_id] = super_auth_servers
|
||||||
page_data["managed_users"] = []
|
page_data["managed_users"] = []
|
||||||
page_data["backgrounds"] = []
|
|
||||||
cached_split = self.controller.cached_login.split("/")
|
|
||||||
|
|
||||||
if len(cached_split) == 1:
|
|
||||||
page_data["backgrounds"].append(
|
|
||||||
self.controller.cached_login
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
page_data["backgrounds"].append(cached_split[1])
|
|
||||||
if "login_1.jpg" not in page_data["backgrounds"]:
|
|
||||||
page_data["backgrounds"].append("login_1.jpg")
|
|
||||||
self.helper.ensure_dir_exists(
|
|
||||||
os.path.join(
|
|
||||||
self.controller.project_root,
|
|
||||||
"app/frontend/static/assets/images/auth/custom",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
for item in os.listdir(
|
|
||||||
os.path.join(
|
|
||||||
self.controller.project_root,
|
|
||||||
"app/frontend/static/assets/images/auth/custom",
|
|
||||||
)
|
|
||||||
):
|
|
||||||
if item not in page_data["backgrounds"]:
|
|
||||||
page_data["backgrounds"].append(item)
|
|
||||||
page_data["background"] = self.controller.cached_login
|
|
||||||
else:
|
else:
|
||||||
page_data["managed_users"] = self.controller.users.get_managed_users(
|
page_data["managed_users"] = self.controller.users.get_managed_users(
|
||||||
exec_user["user_id"]
|
exec_user["user_id"]
|
||||||
@ -895,8 +870,65 @@ class PanelHandler(BaseHandler):
|
|||||||
exec_user["user_id"]
|
exec_user["user_id"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
page_data["active_link"] = "panel_config"
|
||||||
template = "panel/panel_config.html"
|
template = "panel/panel_config.html"
|
||||||
|
|
||||||
|
elif page == "config_json":
|
||||||
|
if exec_user["superuser"]:
|
||||||
|
with open(self.helper.settings_file, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
page_data["config-json"] = data
|
||||||
|
page_data["availables_languages"] = []
|
||||||
|
page_data["all_languages"] = []
|
||||||
|
|
||||||
|
for file in sorted(
|
||||||
|
os.listdir(
|
||||||
|
os.path.join(self.helper.root_dir, "app", "translations")
|
||||||
|
)
|
||||||
|
):
|
||||||
|
if file.endswith(".json"):
|
||||||
|
if file.split(".")[0] not in self.helper.get_setting(
|
||||||
|
"disabled_language_files"
|
||||||
|
):
|
||||||
|
page_data["availables_languages"].append(file.split(".")[0])
|
||||||
|
page_data["all_languages"].append(file.split(".")[0])
|
||||||
|
|
||||||
|
page_data["active_link"] = "config_json"
|
||||||
|
template = "panel/config_json.html"
|
||||||
|
|
||||||
|
elif page == "custom_login":
|
||||||
|
if exec_user["superuser"]:
|
||||||
|
page_data["backgrounds"] = []
|
||||||
|
cached_split = self.controller.cached_login.split("/")
|
||||||
|
|
||||||
|
if len(cached_split) == 1:
|
||||||
|
page_data["backgrounds"].append(self.controller.cached_login)
|
||||||
|
else:
|
||||||
|
page_data["backgrounds"].append(cached_split[1])
|
||||||
|
if "login_1.jpg" not in page_data["backgrounds"]:
|
||||||
|
page_data["backgrounds"].append("login_1.jpg")
|
||||||
|
self.helper.ensure_dir_exists(
|
||||||
|
os.path.join(
|
||||||
|
self.controller.project_root,
|
||||||
|
"app/frontend/static/assets/images/auth/custom",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for item in os.listdir(
|
||||||
|
os.path.join(
|
||||||
|
self.controller.project_root,
|
||||||
|
"app/frontend/static/assets/images/auth/custom",
|
||||||
|
)
|
||||||
|
):
|
||||||
|
if item not in page_data["backgrounds"]:
|
||||||
|
page_data["backgrounds"].append(item)
|
||||||
|
page_data["background"] = self.controller.cached_login
|
||||||
|
page_data[
|
||||||
|
"login_opacity"
|
||||||
|
] = self.controller.management.get_login_opacity()
|
||||||
|
|
||||||
|
page_data["active_link"] = "custom_login"
|
||||||
|
template = "panel/custom_login.html"
|
||||||
|
|
||||||
elif page == "add_user":
|
elif page == "add_user":
|
||||||
page_data["new_user"] = True
|
page_data["new_user"] = True
|
||||||
page_data["user"] = {}
|
page_data["user"] = {}
|
||||||
@ -953,7 +985,9 @@ class PanelHandler(BaseHandler):
|
|||||||
os.listdir(os.path.join(self.helper.root_dir, "app", "translations"))
|
os.listdir(os.path.join(self.helper.root_dir, "app", "translations"))
|
||||||
):
|
):
|
||||||
if file.endswith(".json"):
|
if file.endswith(".json"):
|
||||||
if file not in self.helper.get_setting("disabled_language_files"):
|
if file.split(".")[0] not in self.helper.get_setting(
|
||||||
|
"disabled_language_files"
|
||||||
|
):
|
||||||
if file != str(page_data["languages"][0] + ".json"):
|
if file != str(page_data["languages"][0] + ".json"):
|
||||||
page_data["languages"].append(file.split(".")[0])
|
page_data["languages"].append(file.split(".")[0])
|
||||||
|
|
||||||
@ -1164,7 +1198,9 @@ class PanelHandler(BaseHandler):
|
|||||||
os.listdir(os.path.join(self.helper.root_dir, "app", "translations"))
|
os.listdir(os.path.join(self.helper.root_dir, "app", "translations"))
|
||||||
):
|
):
|
||||||
if file.endswith(".json"):
|
if file.endswith(".json"):
|
||||||
if file not in self.helper.get_setting("disabled_language_files"):
|
if file.split(".")[0] not in self.helper.get_setting(
|
||||||
|
"disabled_language_files"
|
||||||
|
):
|
||||||
if file != str(page_data["languages"][0] + ".json"):
|
if file != str(page_data["languages"][0] + ".json"):
|
||||||
page_data["languages"].append(file.split(".")[0])
|
page_data["languages"].append(file.split(".")[0])
|
||||||
|
|
||||||
@ -1521,10 +1557,6 @@ class PanelHandler(BaseHandler):
|
|||||||
server_obj = self.controller.servers.get_server_obj(server_id)
|
server_obj = self.controller.servers.get_server_obj(server_id)
|
||||||
shutdown_timeout = self.get_argument("shutdown_timeout", 60)
|
shutdown_timeout = self.get_argument("shutdown_timeout", 60)
|
||||||
if superuser:
|
if superuser:
|
||||||
server_path = self.get_argument("server_path", None)
|
|
||||||
if Helpers.is_os_windows():
|
|
||||||
server_path.replace(" ", "^ ")
|
|
||||||
server_path = Helpers.wtol_path(server_path)
|
|
||||||
log_path = self.get_argument("log_path", "")
|
log_path = self.get_argument("log_path", "")
|
||||||
if log_path:
|
if log_path:
|
||||||
if Helpers.is_os_windows():
|
if Helpers.is_os_windows():
|
||||||
@ -1613,7 +1645,7 @@ class PanelHandler(BaseHandler):
|
|||||||
server_obj.shutdown_timeout = shutdown_timeout
|
server_obj.shutdown_timeout = shutdown_timeout
|
||||||
if superuser:
|
if superuser:
|
||||||
if Helpers.validate_traversal(
|
if Helpers.validate_traversal(
|
||||||
self.helper.get_servers_root_dir(), server_path
|
self.helper.get_servers_root_dir(), server_obj.path
|
||||||
):
|
):
|
||||||
server_obj.log_path = log_path
|
server_obj.log_path = log_path
|
||||||
if Helpers.validate_traversal(
|
if Helpers.validate_traversal(
|
||||||
@ -1682,6 +1714,8 @@ class PanelHandler(BaseHandler):
|
|||||||
compress = self.get_argument("compress", False)
|
compress = self.get_argument("compress", False)
|
||||||
shutdown = self.get_argument("shutdown", False)
|
shutdown = self.get_argument("shutdown", False)
|
||||||
check_changed = self.get_argument("changed")
|
check_changed = self.get_argument("changed")
|
||||||
|
before = self.get_argument("backup_before", "")
|
||||||
|
after = self.get_argument("backup_after", "")
|
||||||
if str(check_changed) == str(1):
|
if str(check_changed) == str(1):
|
||||||
checked = self.get_body_arguments("root_path")
|
checked = self.get_body_arguments("root_path")
|
||||||
else:
|
else:
|
||||||
@ -1705,6 +1739,8 @@ class PanelHandler(BaseHandler):
|
|||||||
excluded_dirs=checked,
|
excluded_dirs=checked,
|
||||||
compress=bool(compress),
|
compress=bool(compress),
|
||||||
shutdown=bool(shutdown),
|
shutdown=bool(shutdown),
|
||||||
|
before=before,
|
||||||
|
after=after,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.controller.management.add_to_audit_log(
|
self.controller.management.add_to_audit_log(
|
||||||
@ -1716,6 +1752,38 @@ class PanelHandler(BaseHandler):
|
|||||||
self.tasks_manager.reload_schedule_from_db()
|
self.tasks_manager.reload_schedule_from_db()
|
||||||
self.redirect(f"/panel/server_detail?id={server_id}&subpage=backup")
|
self.redirect(f"/panel/server_detail?id={server_id}&subpage=backup")
|
||||||
|
|
||||||
|
elif page == "config_json":
|
||||||
|
try:
|
||||||
|
data = {}
|
||||||
|
with open(self.helper.settings_file, "r", encoding="utf-8") as f:
|
||||||
|
keys = json.load(f).keys()
|
||||||
|
this_uuid = self.get_argument("uuid")
|
||||||
|
for key in keys:
|
||||||
|
arg_data = self.get_argument(key)
|
||||||
|
if arg_data.startswith(this_uuid):
|
||||||
|
arg_data = arg_data.split(",")
|
||||||
|
arg_data.pop(0)
|
||||||
|
data[key] = arg_data
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
data[key] = int(arg_data)
|
||||||
|
except:
|
||||||
|
if arg_data == "True":
|
||||||
|
data[key] = True
|
||||||
|
elif arg_data == "False":
|
||||||
|
data[key] = False
|
||||||
|
else:
|
||||||
|
data[key] = arg_data
|
||||||
|
with open(self.helper.settings_file, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, indent=4)
|
||||||
|
except Exception as e:
|
||||||
|
logger.critical(
|
||||||
|
"Config File Error: Unable to read "
|
||||||
|
f"{self.helper.settings_file} due to {e}"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.redirect("/panel/config_json")
|
||||||
|
|
||||||
if page == "new_schedule":
|
if page == "new_schedule":
|
||||||
server_id = self.check_server_id()
|
server_id = self.check_server_id()
|
||||||
if not server_id:
|
if not server_id:
|
||||||
|
@ -40,6 +40,7 @@ class PublicHandler(BaseHandler):
|
|||||||
"lang_page": self.helper.get_lang_page(self.helper.get_setting("language")),
|
"lang_page": self.helper.get_lang_page(self.helper.get_setting("language")),
|
||||||
"query": "",
|
"query": "",
|
||||||
"background": self.controller.cached_login,
|
"background": self.controller.cached_login,
|
||||||
|
"login_opacity": self.controller.management.get_login_opacity(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.request.query:
|
if self.request.query:
|
||||||
@ -61,15 +62,15 @@ class PublicHandler(BaseHandler):
|
|||||||
self.clear_cookie("token")
|
self.clear_cookie("token")
|
||||||
# self.clear_cookie("user")
|
# self.clear_cookie("user")
|
||||||
# self.clear_cookie("user_data")
|
# self.clear_cookie("user_data")
|
||||||
self.redirect("/public/login")
|
self.redirect("/login")
|
||||||
return
|
return
|
||||||
|
|
||||||
# if we have no page, let's go to login
|
# if we have no page, let's go to login
|
||||||
else:
|
else:
|
||||||
if self.request.query:
|
if self.request.query:
|
||||||
self.redirect("/public/login?" + self.request.query)
|
self.redirect("/login?" + self.request.query)
|
||||||
else:
|
else:
|
||||||
self.redirect("/public/login")
|
self.redirect("/login")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.render(
|
self.render(
|
||||||
@ -96,9 +97,9 @@ class PublicHandler(BaseHandler):
|
|||||||
|
|
||||||
if page == "login":
|
if page == "login":
|
||||||
|
|
||||||
next_page = "/public/login"
|
next_page = "/login"
|
||||||
if self.request.query:
|
if self.request.query:
|
||||||
next_page = "/public/login?" + self.request.query
|
next_page = "/login?" + self.request.query
|
||||||
|
|
||||||
entered_username = bleach.clean(self.get_argument("username"))
|
entered_username = bleach.clean(self.get_argument("username"))
|
||||||
entered_password = bleach.clean(self.get_argument("password"))
|
entered_password = bleach.clean(self.get_argument("password"))
|
||||||
@ -113,11 +114,9 @@ class PublicHandler(BaseHandler):
|
|||||||
# self.clear_cookie("user_data")
|
# self.clear_cookie("user_data")
|
||||||
self.clear_cookie("token")
|
self.clear_cookie("token")
|
||||||
if self.request.query:
|
if self.request.query:
|
||||||
self.redirect(
|
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}")
|
||||||
f"/public/login?error_msg={error_msg}&{self.request.query}"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.redirect(f"/public/login?error_msg={error_msg}")
|
self.redirect(f"/login?error_msg={error_msg}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# if we don't have a user
|
# if we don't have a user
|
||||||
@ -127,11 +126,9 @@ class PublicHandler(BaseHandler):
|
|||||||
# self.clear_cookie("user_data")
|
# self.clear_cookie("user_data")
|
||||||
self.clear_cookie("token")
|
self.clear_cookie("token")
|
||||||
if self.request.query:
|
if self.request.query:
|
||||||
self.redirect(
|
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}")
|
||||||
f"/public/login?error_msg={error_msg}&{self.request.query}"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.redirect(f"/public/login?error_msg={error_msg}")
|
self.redirect(f"/login?error_msg={error_msg}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# if they are disabled
|
# if they are disabled
|
||||||
@ -144,11 +141,9 @@ class PublicHandler(BaseHandler):
|
|||||||
# self.clear_cookie("user_data")
|
# self.clear_cookie("user_data")
|
||||||
self.clear_cookie("token")
|
self.clear_cookie("token")
|
||||||
if self.request.query:
|
if self.request.query:
|
||||||
self.redirect(
|
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}")
|
||||||
f"/public/login?error_msg={error_msg}&{self.request.query}"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.redirect(f"/public/login?error_msg={error_msg}")
|
self.redirect(f"/login?error_msg={error_msg}")
|
||||||
return
|
return
|
||||||
|
|
||||||
login_result = self.helper.verify_pass(entered_password, user_data.password)
|
login_result = self.helper.verify_pass(entered_password, user_data.password)
|
||||||
@ -187,13 +182,11 @@ class PublicHandler(BaseHandler):
|
|||||||
user_data.user_id, "Tried to log in", 0, self.get_remote_ip()
|
user_data.user_id, "Tried to log in", 0, self.get_remote_ip()
|
||||||
)
|
)
|
||||||
if self.request.query:
|
if self.request.query:
|
||||||
self.redirect(
|
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}")
|
||||||
f"/public/login?error_msg={error_msg}&{self.request.query}"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.redirect(f"/public/login?error_msg={error_msg}")
|
self.redirect(f"/login?error_msg={error_msg}")
|
||||||
else:
|
else:
|
||||||
if self.request.query:
|
if self.request.query:
|
||||||
self.redirect("/public/login?" + self.request.query)
|
self.redirect("/login?" + self.request.query)
|
||||||
else:
|
else:
|
||||||
self.redirect("/public/login")
|
self.redirect("/login")
|
||||||
|
@ -331,7 +331,7 @@ class ServerHandler(BaseHandler):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if import_type == "import_jar":
|
if import_type == "import_jar":
|
||||||
if not self.helper.is_subdir(
|
if self.helper.is_subdir(
|
||||||
import_server_path, self.controller.project_root
|
import_server_path, self.controller.project_root
|
||||||
):
|
):
|
||||||
self.redirect(
|
self.redirect(
|
||||||
|
@ -147,7 +147,6 @@ class Webserver:
|
|||||||
}
|
}
|
||||||
handlers = [
|
handlers = [
|
||||||
(r"/", DefaultHandler, handler_args),
|
(r"/", DefaultHandler, handler_args),
|
||||||
(r"/public/(.*)", PublicHandler, handler_args),
|
|
||||||
(r"/panel/(.*)", PanelHandler, handler_args),
|
(r"/panel/(.*)", PanelHandler, handler_args),
|
||||||
(r"/server/(.*)", ServerHandler, handler_args),
|
(r"/server/(.*)", ServerHandler, handler_args),
|
||||||
(r"/ajax/(.*)", AjaxHandler, handler_args),
|
(r"/ajax/(.*)", AjaxHandler, handler_args),
|
||||||
@ -168,6 +167,9 @@ class Webserver:
|
|||||||
(r"/api/v1/users/delete_user", DeleteUser, handler_args),
|
(r"/api/v1/users/delete_user", DeleteUser, handler_args),
|
||||||
# API Routes V2
|
# API Routes V2
|
||||||
*api_handlers(handler_args),
|
*api_handlers(handler_args),
|
||||||
|
# Using this one at the end
|
||||||
|
# to catch all the other requests to Public Handler
|
||||||
|
(r"/(.*)", PublicHandler, handler_args),
|
||||||
]
|
]
|
||||||
|
|
||||||
app = tornado.web.Application(
|
app = tornado.web.Application(
|
||||||
@ -179,21 +181,14 @@ class Webserver:
|
|||||||
xsrf_cookies=True,
|
xsrf_cookies=True,
|
||||||
autoreload=False,
|
autoreload=False,
|
||||||
log_function=self.log_function,
|
log_function=self.log_function,
|
||||||
login_url="/public/login",
|
login_url="/login",
|
||||||
default_handler_class=PublicHandler,
|
default_handler_class=PublicHandler,
|
||||||
static_handler_class=CustomStaticHandler,
|
static_handler_class=CustomStaticHandler,
|
||||||
serve_traceback=debug_errors,
|
serve_traceback=debug_errors,
|
||||||
)
|
)
|
||||||
http_handers = [
|
http_handers = [
|
||||||
(r"/", HTTPHandler, handler_args),
|
(r"/", HTTPHandler, handler_args),
|
||||||
(r"/public/(.*)", HTTPHandlerPage, handler_args),
|
(r"/(.+)", HTTPHandlerPage, handler_args),
|
||||||
(r"/panel/(.*)", HTTPHandlerPage, handler_args),
|
|
||||||
(r"/server/(.*)", HTTPHandlerPage, handler_args),
|
|
||||||
(r"/ajax/(.*)", HTTPHandlerPage, handler_args),
|
|
||||||
(r"/api/stats/servers", HTTPHandlerPage, handler_args),
|
|
||||||
(r"/api/stats/node", HTTPHandlerPage, handler_args),
|
|
||||||
(r"/ws", HTTPHandlerPage, handler_args),
|
|
||||||
(r"/upload", HTTPHandlerPage, handler_args),
|
|
||||||
]
|
]
|
||||||
http_app = tornado.web.Application(
|
http_app = tornado.web.Application(
|
||||||
http_handers,
|
http_handers,
|
||||||
@ -205,7 +200,7 @@ class Webserver:
|
|||||||
autoreload=False,
|
autoreload=False,
|
||||||
log_function=self.log_function,
|
log_function=self.log_function,
|
||||||
default_handler_class=HTTPHandler,
|
default_handler_class=HTTPHandler,
|
||||||
login_url="/public/login",
|
login_url="/login",
|
||||||
serve_traceback=debug_errors,
|
serve_traceback=debug_errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"major": 4,
|
"major": 4,
|
||||||
"minor": 0,
|
"minor": 0,
|
||||||
"sub": 19
|
"sub": 20
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,59 @@
|
|||||||
<ul class="navbar-nav ml-auto">
|
<ul class="navbar-nav ml-auto">
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link count-indicator">
|
<a class="nav-link count-indicator">
|
||||||
<i class="fas fa-broadcast-tower
|
<i class="fas fa-broadcast-tower
|
||||||
{% if data.get('update_available') %}
|
{% if data.get('update_available') %}
|
||||||
text-danger
|
text-danger
|
||||||
{% end %}
|
{% end %}
|
||||||
"></i>
|
"></i>
|
||||||
<!-- <span class="count bg-success">3</span>-->
|
<!-- <span class="count bg-success">3</span>-->
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link count-indicator" href="/panel/panel_config">
|
<a class="nav-link count-indicator" href="/panel/panel_config">
|
||||||
<i class="fas fa-cogs"></i>
|
<i class="fas fa-cogs"></i>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item dropdown user-dropdown">
|
<li class="nav-item dropdown user-dropdown">
|
||||||
<a class="nav-link dropdown-toggle" id="UserDropdown" href="#" data-toggle="dropdown" aria-expanded="false">
|
<a class="nav-link dropdown-toggle" id="UserDropdown" href="#" data-toggle="dropdown" aria-expanded="false">
|
||||||
<img class="img-xs rounded-circle profile-picture" onerror="pfpError(this)" src="{{ data['user_data']['pfp'] }}" alt="Profile image"> </a>
|
<img class="img-xs rounded-circle profile-picture" onerror="pfpError(this)" src="{{ data['user_data']['pfp'] }}" alt="Profile image"> </a>
|
||||||
<div class="dropdown-menu dropdown-menu-right navbar-dropdown" aria-labelledby="UserDropdown">
|
<div class="dropdown-menu dropdown-menu-right navbar-dropdown" aria-labelledby="UserDropdown">
|
||||||
<div class="dropdown-header text-center">
|
<div class="dropdown-header text-center">
|
||||||
<img class="img-md rounded-circle profile-picture" onerror="pfpError(this)" src="{{ data['user_data']['pfp'] }}" alt="Profile image">
|
<img class="img-md rounded-circle profile-picture" onerror="pfpError(this)" src="{{ data['user_data']['pfp'] }}" alt="Profile image">
|
||||||
<p class="mb-1 mt-3 font-weight-semibold">{{ data['user_data']['username'] }}</p>
|
<p class="mb-1 mt-3 font-weight-semibold">{{ data['user_data']['username'] }}</p>
|
||||||
<p class="font-weight-light text-muted mb-0">Roles: </p>
|
<p class="font-weight-light text-muted mb-0">Roles: </p>
|
||||||
{% for r in data['user_role'] %}
|
{% for r in data['user_role'] %}
|
||||||
<p class="font-weight-light text-muted mb-0">{{ r }}</p>
|
<p class="font-weight-light text-muted mb-0">{{ r }}</p>
|
||||||
{% end %}
|
|
||||||
{% if data.get('api_key') %}
|
|
||||||
<p class="mt-3">Logged in as API key "{{ data['api_key']['name'] }}"</p>
|
|
||||||
{% end %}
|
|
||||||
<p class="font-weight-light text-muted mb-0">Email: {{ data['user_data']['email'] }}</p>
|
|
||||||
</div>
|
|
||||||
{% if data['user_data']['preparing'] %}
|
|
||||||
<span class="dropdown-item" id="support_progress"><i class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs', data['lang']) }}<br><br></span>
|
|
||||||
<span class="dropdown-item" id="support_progress"><div class="support_progress" style="height: 15px; width: 100%;">
|
|
||||||
<div class="progress-bar progress-bar-striped progress-bar-animated" id="logs_progress_bar" role="progressbar" style="width:0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
|
|
||||||
</div></span>
|
|
||||||
{% else %}
|
|
||||||
<a class="dropdown-item" id="support_logs" ><i class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs', data['lang']) }}</i></a>
|
|
||||||
{% end %}
|
{% end %}
|
||||||
{% if data['superuser'] %}
|
{% if data.get('api_key') %}
|
||||||
<a class="dropdown-item" href="/panel/activity_logs"><i class="dropdown-item-icon mdi mdi-calendar-check-outline text-primary"></i>{{ translate('notify', 'activityLog', data['lang']) }}</a>
|
<p class="mt-3">Logged in as API key "{{ data['api_key']['name'] }}"</p>
|
||||||
{% end %}
|
{% end %}
|
||||||
<a class="dropdown-item" href="/public/logout"><i class="dropdown-item-icon mdi mdi-power text-primary"></i>{{ translate('notify', 'logout', data['lang']) }}</a>
|
<p class="font-weight-light text-muted mb-0">Email: {{ data['user_data']['email'] }}</p>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
{% if data['user_data']['preparing'] %}
|
||||||
</ul>
|
<span class="dropdown-item" id="support_progress"><i class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs', data['lang']) }}<br><br></span>
|
||||||
|
<span class="dropdown-item" id="support_progress">
|
||||||
|
<div class="support_progress" style="height: 15px; width: 100%;">
|
||||||
|
<div class="progress-bar progress-bar-striped progress-bar-animated" id="logs_progress_bar" role="progressbar" style="width:0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
{% else %}
|
||||||
|
<a class="dropdown-item" id="support_logs"><i class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs', data['lang']) }}</i></a>
|
||||||
|
{% end %}
|
||||||
|
{% if data['superuser'] %}
|
||||||
|
<a class="dropdown-item" href="/panel/activity_logs"><i class="dropdown-item-icon mdi mdi-calendar-check-outline text-primary"></i>{{ translate('notify', 'activityLog', data['lang']) }}</a>
|
||||||
|
{% end %}
|
||||||
|
<a class="dropdown-item" href="/logout"><i class="dropdown-item-icon mdi mdi-power text-primary"></i>{{ translate('notify', 'logout', data['lang']) }}</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function pfpError(image) {
|
function pfpError(image) {
|
||||||
image.onerror = "";
|
image.onerror = "";
|
||||||
image.src = "/static/assets/images/faces-clipart/pic-3.png";
|
image.src = "/static/assets/images/faces-clipart/pic-3.png";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
318
app/frontend/templates/panel/config_json.html
Normal file
318
app/frontend/templates/panel/config_json.html
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
{% extends ../base.html %}
|
||||||
|
|
||||||
|
{% block meta %}
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% block title %}Crafty Controller - {{ translate('panelConfig', 'pageTitle', data['lang']) }}{% end %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.10/css/bootstrap-select.min.css">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<!-- Page Title Header Starts-->
|
||||||
|
<div class="row page-title-header">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="page-header">
|
||||||
|
<h4 class="page-title">
|
||||||
|
{{ translate('panelConfig', 'title', data['lang']) }}
|
||||||
|
<br />
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Page Title Header Ends-->
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 grid-margin">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
{% if data['superuser'] %}
|
||||||
|
{% include "parts/crafty_config_list.html %}
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
<!-- Page Title Header Starts-->
|
||||||
|
<div class="row page-title-header">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="page-header">
|
||||||
|
<!-- TODO: Translate the following -->
|
||||||
|
<h4 class="page-title">Config.json</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- Page Title Header Ends-->
|
||||||
|
|
||||||
|
<form id="config-form" class="forms-sample" method="post" action="/panel/config_json">
|
||||||
|
{% raw xsrf_form_html() %}
|
||||||
|
|
||||||
|
{% for item in data['config-json'].items() %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form" for="{{item[0]}}">{{item[0]}}
|
||||||
|
<small class="text-muted ml-1">
|
||||||
|
</small> </label><br />
|
||||||
|
{% if item[0] == 'language' %}
|
||||||
|
<select name="{{item[0]}}" class="form-control">
|
||||||
|
{% for lang in data['availables_languages'] %}
|
||||||
|
{% if lang == item[1] %}
|
||||||
|
<option selected>{{lang}}</option>
|
||||||
|
{% else %}
|
||||||
|
<option>{{lang}}</option>
|
||||||
|
{% end %}
|
||||||
|
{% end %}
|
||||||
|
</select>
|
||||||
|
{% elif item[0] == 'disabled_language_files' %}
|
||||||
|
<div class="input-group">
|
||||||
|
<button type="button" class="btn btn-outline-default custom-picker" onclick="$('option', $('#lang_select')).each(function(element) {
|
||||||
|
$(this).removeAttr('selected').prop('selected', false); $('.selectpicker').selectpicker('refresh')
|
||||||
|
});">Enable all Languages</button>
|
||||||
|
<select id="lang_select" class="form-control selectpicker show-tick" data-icon-base="fas" data-tick-icon="fa-check" multiple data-style="custom-picker">
|
||||||
|
{% for lang in data['all_languages'] %}
|
||||||
|
{% if lang in item[1] %}
|
||||||
|
<option selected>{{lang}}</option>
|
||||||
|
{% else %}
|
||||||
|
<option>{{lang}}</option>
|
||||||
|
{% end %}
|
||||||
|
{% end %}
|
||||||
|
</select>
|
||||||
|
<textarea id="disabled_lang" name="{{item[0]}}" class="form-control list hidden" rows="{{ len(data['all_languages']) }}" value="{{','.join(item[1])}}" hidden>{{','.join(item[1])}}</textarea>
|
||||||
|
</div>
|
||||||
|
{% elif isinstance(item[1], list) %}
|
||||||
|
<textarea value="{{','.join(item[1])}}" type="text" name="{{item[0]}}" class="form-control list">{{','.join(item[1])}}</textarea>
|
||||||
|
{% elif isinstance(item[1], bool) %}
|
||||||
|
{% if item[1] == True %}
|
||||||
|
<div style="margin-left: 30px;">
|
||||||
|
<input type="radio" class="form-check-input" name="{{item[0]}}" id="True" value="True" checked>
|
||||||
|
<label for="True">True</label><br>
|
||||||
|
<input type="radio" class="form-check-input" name="{{item[0]}}" id="False" value="False">
|
||||||
|
<label for="False">False</label>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div style="margin-left: 30px;">
|
||||||
|
<input type="radio" class="form-check-input" name="{{item[0]}}" id="True" value="True">
|
||||||
|
<label for="True">True</label><br>
|
||||||
|
<input type="radio" class="form-check-input" name="{{item[0]}}" id="False" value="False" checked>
|
||||||
|
<label for="False">False</label>
|
||||||
|
</div>
|
||||||
|
{% end %}
|
||||||
|
{% elif isinstance(item[1], int) %}
|
||||||
|
<input type="number" class="form-control" name="{{item[0]}}" id="{{item[0]}}" value="{{ item[1] }}" step="1" min="0" required>
|
||||||
|
{% else %}
|
||||||
|
<input type="text" class="form-control" name="{{item[0]}}" id="{{item[0]}}" value="{{ item[1] }}" step="2" min="0" required>
|
||||||
|
{% end %}
|
||||||
|
</div>
|
||||||
|
{% end %}
|
||||||
|
<button class="btn btn-success" type="submit">Submit</button> <span id="submit-status"></span>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.custom-picker {
|
||||||
|
border: 1px solid var(--outline);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu.inner {
|
||||||
|
display: inline-block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-body {
|
||||||
|
color: white !important;
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"] {
|
||||||
|
-ms-transform: scale(1.5);
|
||||||
|
/* IE 9 */
|
||||||
|
-webkit-transform: scale(1.5);
|
||||||
|
/* Chrome, Safari, Opera */
|
||||||
|
transform: scale(1.5);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- content-wrapper ends -->
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
<script>
|
||||||
|
$("#config-form").submit(function (e) {
|
||||||
|
let uuid = uuidv4();
|
||||||
|
var token = getCookie("_xsrf")
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
$("#submit-status").html('<i class="fa fa-spinner fa-spin"></i>');
|
||||||
|
/* Convert multiple select to text list */
|
||||||
|
let selected_Lang = $('#lang_select').val();
|
||||||
|
$('#disabled_lang').val(selected_Lang);
|
||||||
|
|
||||||
|
let class_list = document.getElementsByClassName("list");
|
||||||
|
let form_json = convertFormToJSON($("#config-form"));
|
||||||
|
for (let i = 0; i < class_list.length; i++) {
|
||||||
|
let str = String($(class_list.item(i)).val())
|
||||||
|
form_json[$(class_list.item(i)).attr("name")] = uuid + "," + str.replace(/\s/g, '');
|
||||||
|
};
|
||||||
|
form_json['uuid'] = uuid;
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
headers: { 'X-XSRFToken': token },
|
||||||
|
dataType: "text",
|
||||||
|
url: '/panel/config_json',
|
||||||
|
data: form_json,
|
||||||
|
success: function (data) {
|
||||||
|
$("#submit-status").html('<i class="fa fa-check"></i>');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function uuidv4() {
|
||||||
|
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
|
||||||
|
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function convertFormToJSON(form) {
|
||||||
|
const array = $(form).serializeArray(); // Encodes the set of form elements as an array of names and values.
|
||||||
|
const json = {};
|
||||||
|
$.each(array, function () {
|
||||||
|
json[this.name] = this.value || "";
|
||||||
|
});
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('[data-toggle="popover"]').popover();
|
||||||
|
if ($(window).width() < 1000) {
|
||||||
|
$('.too_small').popover("show");
|
||||||
|
$('.too_small2').popover("show");
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
$(window).ready(function () {
|
||||||
|
$('body').click(function () {
|
||||||
|
$('.too_small').popover("hide");
|
||||||
|
$('.too_small2').popover("hide");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$(window).resize(function () {
|
||||||
|
// This will execute whenever the window is resized
|
||||||
|
if ($(window).width() < 1000) {
|
||||||
|
$('.too_small').popover("show");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('.too_small').popover("hide");
|
||||||
|
} // New width
|
||||||
|
if ($(window).width() < 1000) {
|
||||||
|
$('.too_small2').popover("show");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('.too_small2').popover("hide");
|
||||||
|
} // New width
|
||||||
|
});
|
||||||
|
$('#file').change(function () {
|
||||||
|
console.log("File changed");
|
||||||
|
if ($('#file').val()) {
|
||||||
|
$('#upload-button').prop("disabled", false);
|
||||||
|
console.log("File changed good");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
console.log('ready for JS!');
|
||||||
|
$('.selectpicker').selectpicker("refresh");
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".show_button").click(function () {
|
||||||
|
console.log("showing key");
|
||||||
|
api_key = $(this).attr("data-id");
|
||||||
|
bootbox.alert({
|
||||||
|
backdrop: true,
|
||||||
|
title: '',
|
||||||
|
message: api_key,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.clear-comm').click(function () {
|
||||||
|
var token = getCookie("_xsrf")
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
headers: { 'X-XSRFToken': token },
|
||||||
|
url: '/ajax/clear_comm',
|
||||||
|
success: function (data) {
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
$('.delete-photo').click(function () {
|
||||||
|
var token = getCookie("_xsrf")
|
||||||
|
let photo = $('#photo').find(":selected").val();
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
headers: { 'X-XSRFToken': token },
|
||||||
|
url: '/ajax/delete_photo?photo=' + photo,
|
||||||
|
success: function (data) {
|
||||||
|
location.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
$('.select-photo').click(function () {
|
||||||
|
var token = getCookie("_xsrf")
|
||||||
|
let photo = $('#photo').find(":selected").val();
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
headers: { 'X-XSRFToken': token },
|
||||||
|
url: '/ajax/select_photo?photo=' + photo,
|
||||||
|
success: function (data) {
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
var file;
|
||||||
|
function sendFile() {
|
||||||
|
file = $("#file")[0].files[0]
|
||||||
|
document.getElementById("upload_input").innerHTML = '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"> <i class="fa-solid fa-spinner"></i></div></div>'
|
||||||
|
let xmlHttpRequest = new XMLHttpRequest();
|
||||||
|
let token = getCookie("_xsrf")
|
||||||
|
let fileName = file.name
|
||||||
|
let target = '/upload'
|
||||||
|
let mimeType = file.type
|
||||||
|
let size = file.size
|
||||||
|
let type = 'background'
|
||||||
|
|
||||||
|
xmlHttpRequest.open('POST', target, true);
|
||||||
|
xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType);
|
||||||
|
xmlHttpRequest.setRequestHeader('X-XSRFToken', token);
|
||||||
|
xmlHttpRequest.setRequestHeader('X-Content-Length', size);
|
||||||
|
xmlHttpRequest.setRequestHeader('X-Content-Disposition', 'attachment; filename="' + fileName + '"');
|
||||||
|
xmlHttpRequest.setRequestHeader('X-Content-Upload-Type', type);
|
||||||
|
xmlHttpRequest.setRequestHeader('X-FileName', fileName);
|
||||||
|
xmlHttpRequest.addEventListener('load', (event) => {
|
||||||
|
if (event.target.responseText == 'success') {
|
||||||
|
console.log('Upload for file', file.name, 'was successful!')
|
||||||
|
document.getElementById("upload_input").innerHTML = '<div class="card-header header-sm d-flex justify-content-between align-items-center"><span id="file-uploaded" style="color: gray;">' + fileName + '</span> 🔒</div>';
|
||||||
|
setTimeout(function () {
|
||||||
|
window.location.reload();
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
alert('Upload failed with response: ' + event.target.responseText);
|
||||||
|
doUpload = false;
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
xmlHttpRequest.addEventListener('error', (e) => {
|
||||||
|
console.error('Error while uploading file', file.name + '.', 'Event:', e)
|
||||||
|
}, false);
|
||||||
|
xmlHttpRequest.send(file);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.10/js/bootstrap-select.min.js">
|
||||||
|
</script>
|
||||||
|
{% end %}
|
391
app/frontend/templates/panel/custom_login.html
Normal file
391
app/frontend/templates/panel/custom_login.html
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
{% extends ../base.html %}
|
||||||
|
|
||||||
|
{% block meta %}
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% block title %}Crafty Controller - {{ translate('customLogin', 'pageTitle', data['lang']) }}{% end %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<!-- Page Title Header Starts-->
|
||||||
|
<div class="row page-title-header">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="page-header">
|
||||||
|
<h4 class="page-title">
|
||||||
|
{{ translate('panelConfig', 'title', data['lang']) }}
|
||||||
|
<br />
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Page Title Header Ends-->
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 grid-margin">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
|
||||||
|
{% if data['superuser'] %}
|
||||||
|
{% include "parts/crafty_config_list.html %}
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
<!-- Page Title Header Starts-->
|
||||||
|
<div class="row page-title-header">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="page-header">
|
||||||
|
<!-- TODO: Translate the following -->
|
||||||
|
<h4 class="page-title">{{ translate('customLogin', 'customLoginPage', data['lang']) }}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- Page Title Header Ends-->
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<h4>{{ translate('customLogin', 'loginImage', data['lang']) }}</h4>
|
||||||
|
<hr>
|
||||||
|
<form class="form-row" name="zip" method="post" class="server-wizard" onSubmit="wait_msg(true)">
|
||||||
|
{% raw xsrf_form_html() %}
|
||||||
|
<input type="hidden" value="import_zip" name="create_type">
|
||||||
|
<div class="col form-group">
|
||||||
|
<span id="upload_input"><input type="file" class="form-control-file" id="file" name="file"
|
||||||
|
multiple="false" required></span>
|
||||||
|
</div>
|
||||||
|
<div class="col form-group">
|
||||||
|
<button type="button" class="btn btn-info" id="upload-button" onclick="sendFile()"
|
||||||
|
disabled>UPLOAD</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<hr>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<div>
|
||||||
|
<h6>{{ translate('customLogin', 'preview', data['lang']) }}:</h6>
|
||||||
|
<form id="photo_form">
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="photo" class="col-sm-6 col-form-label">Selected Background Image</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<select class="form-select form-control form-control-lg select-css form-control-plaintext"
|
||||||
|
id="photo" name="photo" form="photo_form" onchange="updateBackgroundPreview()">
|
||||||
|
{% for image in data["backgrounds"] %}
|
||||||
|
<option value="{{image}}">{{image}}</option>
|
||||||
|
{% end %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="photo_loading" class="form-group" hidden>
|
||||||
|
<div class="progress">
|
||||||
|
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar"
|
||||||
|
aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"> <i
|
||||||
|
class="fa-solid fa-spinner"></i></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-sm-3" for="formControlRange">{{ translate('customLogin', 'loginOpacity',
|
||||||
|
data['lang']) }}</label>
|
||||||
|
<label class="col-sm-1" id="opacityValue">{{ data['login_opacity'] }}%</label>
|
||||||
|
<div class="range col-sm-8">
|
||||||
|
<input type="range" class="form-control-range" id="modal_opacity" name="modal_opacity"
|
||||||
|
onchange="previewOpacity()" min="0" max="100" value="{{ data['login_opacity'] }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="login_preview" style="position: relative;">
|
||||||
|
<img id="bg-preview" src="../../static/assets/images/auth/{{ data['background'] }}"
|
||||||
|
class="img-fluid" alt="Responsive image">
|
||||||
|
<div id="login-form-preview">
|
||||||
|
<div id="login-form-background" class="auto-form-wrapper login-modal">
|
||||||
|
<div class="text-center auto-form-logo">
|
||||||
|
<img src="/static/assets/images/logo_long.svg">
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
#login-form-preview {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
overflow: hidden;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%) translateY(-50%);
|
||||||
|
max-width: 90%;
|
||||||
|
max-height: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auto-form-wrapper {
|
||||||
|
background: rgb(34, 36, 55, 1);
|
||||||
|
padding: 2rem 2rem 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
-webkit-box-shadow: 0 -25px 37.7px 11.3px rgb(8 143 220 / 7%);
|
||||||
|
box-shadow: 0 -25px 37.7px 11.3px rgb(8 143 220 / 7%);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*.auto-form-logo {
|
||||||
|
background: #222437;
|
||||||
|
padding: 0rem;
|
||||||
|
margin: 0.5rem 0rem;
|
||||||
|
border-radius: 0.2rem;
|
||||||
|
color: #fff;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
.login-modal {
|
||||||
|
border-radius: 0.4rem !important;
|
||||||
|
box-shadow: 0 8px 12px 0 hsla(0, 0%, 0%, 0.2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-text-input {
|
||||||
|
border: none !important;
|
||||||
|
background-color: hsl(234, 30%, 45%);
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-text-input:hover,
|
||||||
|
.login-text-input:focus {
|
||||||
|
background-color: hsl(234, 30%, 39%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-input {
|
||||||
|
border-radius: 0.4rem !important;
|
||||||
|
box-shadow: 0 8px 12px 0 hsla(0, 0%, 0%, 0.2);
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-input:hover,
|
||||||
|
.login-input:focus {
|
||||||
|
box-shadow: 0 12px 16px 0 hsla(0, 0%, 0%, 0.4);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="login_form_data">
|
||||||
|
<input type="hidden" name="_xsrf"
|
||||||
|
value="2|1d603267|809fb6bd82f677d440e484dde7c3a310|1671726040" disabled>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="label">Username</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control login-text-input login-input"
|
||||||
|
placeholder="Username" name="username" id="username" required="true" disabled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="label">Password</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="password" class="form-control login-text-input login-input"
|
||||||
|
placeholder="Password" name="password" id="password" required="true" disabled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button class="login-input btn btn-primary submit-btn btn-block" disabled>Log
|
||||||
|
In</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<fieldset style="color: red; text-align: center;">
|
||||||
|
<span></span>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="form-group d-flex justify-content-between">
|
||||||
|
<div class="form-check form-check-flat mt-0">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<a href="#" class="text-small forgot-password" disabled>Forgot Password</a>
|
||||||
|
</div>
|
||||||
|
<div class="text-block text-center my-3">
|
||||||
|
<span class="text-small font-weight-semibold"><a
|
||||||
|
href="https://craftycontrol.com/">Crafty Control
|
||||||
|
4.0.20</a> </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button class="btn btn-outline-success select-photo" type="button">{{
|
||||||
|
translate('customLogin',
|
||||||
|
'apply', data['lang']) }}</button>
|
||||||
|
<button class="btn btn-outline-danger delete-photo" type="button">{{
|
||||||
|
translate('customLogin',
|
||||||
|
'delete', data['lang']) }}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.popover-body {
|
||||||
|
color: white !important;
|
||||||
|
;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- content-wrapper ends -->
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('[data-toggle="popover"]').popover();
|
||||||
|
if ($(window).width() < 1000) {
|
||||||
|
$('.too_small').popover("show");
|
||||||
|
$('.too_small2').popover("show");
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
$(window).ready(function () {
|
||||||
|
$('body').click(function () {
|
||||||
|
$('.too_small').popover("hide");
|
||||||
|
$('.too_small2').popover("hide");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$(window).resize(function () {
|
||||||
|
// This will execute whenever the window is resized
|
||||||
|
if ($(window).width() < 1000) {
|
||||||
|
$('.too_small').popover("show");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('.too_small').popover("hide");
|
||||||
|
} // New width
|
||||||
|
if ($(window).width() < 1000) {
|
||||||
|
$('.too_small2').popover("show");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('.too_small2').popover("hide");
|
||||||
|
} // New width
|
||||||
|
});
|
||||||
|
$('#file').change(function () {
|
||||||
|
console.log("File changed");
|
||||||
|
if ($('#file').val()) {
|
||||||
|
$('#upload-button').prop("disabled", false);
|
||||||
|
console.log("File changed good");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
console.log('ready for JS!')
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".show_button").click(function () {
|
||||||
|
console.log("showing key");
|
||||||
|
api_key = $(this).attr("data-id");
|
||||||
|
bootbox.alert({
|
||||||
|
backdrop: true,
|
||||||
|
title: '',
|
||||||
|
message: api_key,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.delete-photo').click(function () {
|
||||||
|
var token = getCookie("_xsrf")
|
||||||
|
let photo = $('#photo').find(":selected").val();
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
headers: { 'X-XSRFToken': token },
|
||||||
|
url: '/ajax/delete_photo?photo=' + encodeURIComponent(photo),
|
||||||
|
success: function (data) {
|
||||||
|
location.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
$('.select-photo').click(function () {
|
||||||
|
var token = getCookie("_xsrf")
|
||||||
|
let photo = $('#photo').find(":selected").val();
|
||||||
|
let opacity = $('#modal_opacity').val();
|
||||||
|
let enc_photo = encodeURIComponent(photo);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
headers: { 'X-XSRFToken': token },
|
||||||
|
url: '/ajax/select_photo?photo=' + enc_photo + '&opacity=' + opacity,
|
||||||
|
success: function (data) {
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
let opacity = parseInt($("#modal_opacity").val());
|
||||||
|
document.getElementById('login-form-background').style.background = 'rgb(34, 36, 55, ' + (opacity / 100) + ')';
|
||||||
|
});
|
||||||
|
|
||||||
|
function previewOpacity() {
|
||||||
|
let opacity = parseInt($("#modal_opacity").val())
|
||||||
|
console.debug("Selected Opacity = " + opacity + "%");
|
||||||
|
document.getElementById('opacityValue').innerHTML = (opacity) + "%";
|
||||||
|
document.getElementById('login-form-background').style.background = 'rgb(34, 36, 55, ' + (opacity / 100) + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBackgroundSelect() {
|
||||||
|
$("#photo").val($("#try_photo").val()).change();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBackgroundPreview() {
|
||||||
|
var img = document.getElementById('bg-preview');
|
||||||
|
if ($("#photo").val() == "login_1.jpg") {
|
||||||
|
var src_path = "../../static/assets/images/auth/".concat($("#photo").val());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var src_path = "../../static/assets/images/auth/custom/".concat($("#photo").val());
|
||||||
|
}
|
||||||
|
img.src = src_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
var file;
|
||||||
|
function sendFile() {
|
||||||
|
file = $("#file")[0].files[0]
|
||||||
|
document.getElementById("upload_input").innerHTML = '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"> <i class="fa-solid fa-spinner"></i></div></div>';
|
||||||
|
let xmlHttpRequest = new XMLHttpRequest();
|
||||||
|
let token = getCookie("_xsrf")
|
||||||
|
let fileName = file.name
|
||||||
|
let target = '/upload'
|
||||||
|
let mimeType = file.type
|
||||||
|
let size = file.size
|
||||||
|
let type = 'background'
|
||||||
|
|
||||||
|
xmlHttpRequest.open('POST', target, true);
|
||||||
|
xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType);
|
||||||
|
xmlHttpRequest.setRequestHeader('X-XSRFToken', token);
|
||||||
|
xmlHttpRequest.setRequestHeader('X-Content-Length', size);
|
||||||
|
xmlHttpRequest.setRequestHeader('X-Content-Disposition', 'attachment; filename="' + fileName + '"');
|
||||||
|
xmlHttpRequest.setRequestHeader('X-Content-Upload-Type', type);
|
||||||
|
xmlHttpRequest.setRequestHeader('X-FileName', fileName);
|
||||||
|
xmlHttpRequest.addEventListener('load', (event) => {
|
||||||
|
if (event.target.responseText == 'success') {
|
||||||
|
console.log('Upload for file', file.name, 'was successful!')
|
||||||
|
document.getElementById("upload_input").innerHTML = '<div class="card-header header-sm d-flex justify-content-between align-items-center"><span id="file-uploaded" style="color: gray;">' + fileName + '</span> 🔒</div>';
|
||||||
|
setTimeout(function () {
|
||||||
|
window.location.reload();
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
alert('Upload failed with response: ' + event.target.responseText);
|
||||||
|
doUpload = false;
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
xmlHttpRequest.addEventListener('error', (e) => {
|
||||||
|
console.error('Error while uploading file', file.name + '.', 'Event:', e)
|
||||||
|
}, false);
|
||||||
|
xmlHttpRequest.send(file);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% end %}
|
@ -282,184 +282,184 @@
|
|||||||
<span class="port" data-toggle="tooltip" title="{{
|
<span class="port" data-toggle="tooltip" title="{{
|
||||||
server['server_data']['server_port'] }}">
|
server['server_data']['server_port'] }}">
|
||||||
<div id="server_running_status_{{server['server_data']['server_id']}}">
|
<div id="server_running_status_{{server['server_data']['server_id']}}">
|
||||||
{% if server['stats']['running'] %}
|
{% if server['stats']['running'] %}
|
||||||
<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online',
|
<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online',
|
||||||
data['lang']) }}</span>
|
data['lang']) }}</span>
|
||||||
{% elif server['stats']['crashed'] %}
|
{% elif server['stats']['crashed'] %}
|
||||||
<span class="text-danger"><i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard',
|
<span class="text-danger"><i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard',
|
||||||
'crashed',
|
'crashed',
|
||||||
data['lang']) }}</span>
|
data['lang']) }}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline',
|
<span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline',
|
||||||
data['lang']) }}</span>
|
data['lang']) }}</span>
|
||||||
{% end %}
|
{% end %}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
</td>
|
</td>
|
||||||
<span class="server-player-totals" id="server_players_{{server['server_data']['server_id']}}"
|
<span class="server-player-totals" id="server_players_{{server['server_data']['server_id']}}"
|
||||||
data-players="{{ server['stats']['online']}}" data-max="{{ server['stats']['max'] }}"></span>
|
data-players="{{ server['stats']['online']}}" data-max="{{ server['stats']['max'] }}"></span>
|
||||||
</tr>
|
</tr>
|
||||||
{% end %}
|
{% end %}
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
{% for server in data['failed_servers'] %}
|
|
||||||
<tr id="{{server['server_id']}}" draggable="false">
|
|
||||||
<td class="text-warning"><i class="fas fa-server"></i> <a class="text-warning"
|
|
||||||
href="/panel/server_detail?id={{server['server_id']}}&subpage=config">{{server['server_name']}}</a>
|
|
||||||
</td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td><i class="fas fa-cloud"></i> Unloaded</td>
|
|
||||||
</tr>
|
|
||||||
{% end %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
</span>
|
||||||
|
{% for server in data['failed_servers'] %}
|
||||||
|
<tr id="{{server['server_id']}}" draggable="false">
|
||||||
|
<td class="text-warning"><i class="fas fa-server"></i> <a class="text-warning"
|
||||||
|
href="/panel/server_detail?id={{server['server_id']}}&subpage=config">{{server['server_name']}}</a>
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td><i class="fas fa-cloud"></i> Unloaded</td>
|
||||||
|
</tr>
|
||||||
{% end %}
|
{% end %}
|
||||||
{% if len(data['servers']) > 0 %}
|
</tbody>
|
||||||
<!-- View for Small screen -->
|
</table>
|
||||||
<div class="d-sm-none d-block">
|
</div>
|
||||||
<div class="accordion" id="accordionServers">
|
{% end %}
|
||||||
{% for server in data['servers'] %}
|
{% if len(data['servers']) > 0 %}
|
||||||
<div class="card">
|
<!-- View for Small screen -->
|
||||||
<div class="card-header" id="heading-{{server['server_data']['server_id']}}">
|
<div class="d-sm-none d-block">
|
||||||
<h2 class="mb-0 container overflow-hidden">
|
<div class="accordion" id="accordionServers">
|
||||||
<div class="row">
|
{% for server in data['servers'] %}
|
||||||
<div class="col-10 col-lg-3 mx-0 px-0">
|
<div class="card">
|
||||||
{% if server['alert'] %}
|
<div class="card-header" id="heading-{{server['server_data']['server_id']}}">
|
||||||
<a style="color: red !important" class="btn btn-link d-flex justify-content-start" type="button"
|
<h2 class="mb-0 container overflow-hidden">
|
||||||
href="/panel/server_detail?id={{server['server_data']['server_id']}}">
|
<div class="row">
|
||||||
<i class="fas fa-server"></i> {{ server['server_data']['server_name'] }} <i
|
<div class="col-10 col-lg-3 mx-0 px-0">
|
||||||
class="fas fa-exclamation-triangle"></i>
|
{% if server['alert'] %}
|
||||||
</a>
|
<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>
|
||||||
|
<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 %}
|
{% else %}
|
||||||
<a class="btn btn-link d-flex justify-content-start" type="button"
|
<span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline',
|
||||||
href="/panel/server_detail?id={{server['server_data']['server_id']}}">
|
data['lang']) }}</span>
|
||||||
<i class="fas fa-server"></i> {{ server['server_data']['server_name'] }}
|
{% end %}
|
||||||
</a>
|
</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>
|
||||||
|
{% 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']['importing']%}
|
||||||
|
<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', 'importing', 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 %}
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
<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>
|
|
||||||
{% 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']['importing']%}
|
|
||||||
<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', 'importing', 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>
|
</div>
|
||||||
</h2>
|
</div>
|
||||||
</div>
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="collapse-{{server['server_data']['server_id']}}" class="collapse"
|
<div id="collapse-{{server['server_data']['server_id']}}" class="collapse"
|
||||||
aria-labelledby="heading-{{server['server_data']['server_id']}}" data-parent="#accordionServers">
|
aria-labelledby="heading-{{server['server_data']['server_id']}}" data-parent="#accordionServers">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<h6>{{ translate('dashboard', 'cpuUsage', data['lang']) }}</h6>
|
<h6>{{ translate('dashboard', 'cpuUsage', data['lang']) }}</h6>
|
||||||
<div id="m_server_cpu_{{server['server_data']['server_id']}}">
|
<div id="m_server_cpu_{{server['server_data']['server_id']}}">
|
||||||
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
||||||
title="{{server['stats']['cpu']}}">
|
title="{{server['stats']['cpu']}}">
|
||||||
<div class="progress-bar
|
<div class="progress-bar
|
||||||
{% if server['stats']['cpu'] <= 33 %}
|
{% if server['stats']['cpu'] <= 33 %}
|
||||||
bg-success
|
bg-success
|
||||||
{% elif 34 <= server['stats']['cpu'] <= 66 %}
|
{% elif 34 <= server['stats']['cpu'] <= 66 %}
|
||||||
@ -468,17 +468,17 @@
|
|||||||
bg-danger
|
bg-danger
|
||||||
{% end %}
|
{% end %}
|
||||||
" role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0" aria-valuemin="0"
|
" role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0" aria-valuemin="0"
|
||||||
aria-valuemax="100"></div>
|
aria-valuemax="100"></div>
|
||||||
</div>
|
|
||||||
{{server['stats']['cpu']}}%
|
|
||||||
</div>
|
</div>
|
||||||
|
{{server['stats']['cpu']}}%
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
</div>
|
||||||
<h6>{{ translate('dashboard', 'memUsage', data['lang']) }}</h6>
|
<div class="col-6">
|
||||||
<div draggable="false" id="m_server_mem_{{server['server_data']['server_id']}}">
|
<h6>{{ translate('dashboard', 'memUsage', data['lang']) }}</h6>
|
||||||
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
<div draggable="false" id="m_server_mem_{{server['server_data']['server_id']}}">
|
||||||
title="{{server['stats']['mem']}}">
|
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
||||||
<div class="progress-bar
|
title="{{server['stats']['mem']}}">
|
||||||
|
<div class="progress-bar
|
||||||
{% if server['stats']['mem_percent'] <= 33 %}
|
{% if server['stats']['mem_percent'] <= 33 %}
|
||||||
bg-success
|
bg-success
|
||||||
{% elif 34 <= server['stats']['mem_percent'] <= 66 %}
|
{% elif 34 <= server['stats']['mem_percent'] <= 66 %}
|
||||||
@ -487,58 +487,58 @@
|
|||||||
bg-danger
|
bg-danger
|
||||||
{% end %}
|
{% end %}
|
||||||
" role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0"
|
" role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0"
|
||||||
aria-valuemin="0" aria-valuemax="100"></div>
|
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>
|
||||||
|
{{server['stats']['mem_percent']}}% -
|
||||||
|
|
||||||
|
{% if server['stats']['mem'] == 0 %}
|
||||||
|
0 MB
|
||||||
|
{% else %}
|
||||||
|
{{server['stats']['mem']}}
|
||||||
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
</div>
|
||||||
<div class="row">
|
<br />
|
||||||
<div class="col-6">
|
<div class="row">
|
||||||
<h6>{{ translate('dashboard', 'size', data['lang']) }}</h6>
|
<div class="col-6">
|
||||||
<div draggable="false" id="m_server_world_{{server['server_data']['server_id']}}">
|
<h6>{{ translate('dashboard', 'size', data['lang']) }}</h6>
|
||||||
{{ server['stats']['world_size'] }}
|
<div draggable="false" id="m_server_world_{{server['server_data']['server_id']}}">
|
||||||
</div>
|
{{ server['stats']['world_size'] }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6" style="width: auto;">
|
</div>
|
||||||
<h6>{{ translate('dashboard', 'players', data['lang']) }}</h6>
|
<div class="col-6" style="width: auto;">
|
||||||
<div draggable="false" id="m_server_desc_{{server['server_data']['server_id']}}">
|
<h6>{{ translate('dashboard', 'players', data['lang']) }}</h6>
|
||||||
{% if server['stats']['int_ping_results'] %}
|
<div draggable="false" id="m_server_desc_{{server['server_data']['server_id']}}">
|
||||||
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard',
|
{% if server['stats']['int_ping_results'] %}
|
||||||
'max',
|
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard',
|
||||||
data['lang']) }} <br />
|
'max',
|
||||||
|
data['lang']) }} <br />
|
||||||
|
|
||||||
{% if server['stats']['desc'] != 'False' %}
|
{% if server['stats']['desc'] != 'False' %}
|
||||||
<div id="desc_id"
|
<div id="desc_id"
|
||||||
style="overflow-wrap: break-word !important; max-width: 85px !important; overflow: scroll;">
|
style="overflow-wrap: break-word !important; max-width: 85px !important; overflow: scroll;">
|
||||||
{{ server['stats']['desc'] }}</div> <br />
|
{{ server['stats']['desc'] }}</div> <br />
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
{% if server['stats']['version'] != 'False' %}
|
{% if server['stats']['version'] != 'False' %}
|
||||||
{{ server['stats']['version'] }}
|
{{ server['stats']['version'] }}
|
||||||
{% end %}
|
{% end %}
|
||||||
{% end %}
|
{% end %}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% end %}
|
|
||||||
</div>
|
</div>
|
||||||
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
{% end %}
|
|
||||||
</div>
|
</div>
|
||||||
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -606,7 +606,6 @@
|
|||||||
function send_command(server_id, command) {
|
function send_command(server_id, command) {
|
||||||
/* this getCookie function is in base.html */
|
/* this getCookie function is in base.html */
|
||||||
const token = getCookie("_xsrf");
|
const token = getCookie("_xsrf");
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
headers: { 'X-XSRFToken': token },
|
headers: { 'X-XSRFToken': token },
|
||||||
@ -623,6 +622,51 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function warn(message, link = null, className = null) {
|
||||||
|
var closeEl = document.createElement('span');
|
||||||
|
var strongEL = document.createElement('strong');
|
||||||
|
var msgEl = document.createElement('div');
|
||||||
|
|
||||||
|
closeEl.innerHTML = '×';
|
||||||
|
strongEL.textContent = 'Warning: ';
|
||||||
|
msgEl.append(strongEL, message);
|
||||||
|
|
||||||
|
|
||||||
|
closeEl.style.marginLeft = '15px';
|
||||||
|
closeEl.style.fontWeight = 'bold';
|
||||||
|
closeEl.style.float = 'right';
|
||||||
|
closeEl.style.fontSize = '22px';
|
||||||
|
closeEl.style.lineHeight = '20px';
|
||||||
|
closeEl.style.cursor = 'pointer';
|
||||||
|
|
||||||
|
closeEl.addEventListener('click', function () { this.parentElement.style.display = 'none'; });
|
||||||
|
|
||||||
|
var parentEl = document.createElement('div');
|
||||||
|
|
||||||
|
parentEl.style.padding = '20px';
|
||||||
|
parentEl.style.backgroundColor = '#f7970f';
|
||||||
|
|
||||||
|
parentEl.appendChild(closeEl);
|
||||||
|
parentEl.appendChild(msgEl);
|
||||||
|
if (link) {
|
||||||
|
let linkEl = document.createElement('a')
|
||||||
|
linkEl.href = link;
|
||||||
|
linkEl.innerHTML = "See our documentation for details.";
|
||||||
|
linkEl.style.color = 'white';
|
||||||
|
linkEl.style.textDecoration = 'underline';
|
||||||
|
linkEl.target = "_blank";
|
||||||
|
|
||||||
|
parentEl.appendChild(linkEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (className) {
|
||||||
|
parentEl.classList.add(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector('.dynamicMsg').appendChild(parentEl);
|
||||||
|
}
|
||||||
|
|
||||||
function send_kill(server_id) {
|
function send_kill(server_id) {
|
||||||
/* this getCookie function is in base.html */
|
/* this getCookie function is in base.html */
|
||||||
const token = getCookie("_xsrf");
|
const token = getCookie("_xsrf");
|
||||||
@ -774,11 +818,15 @@
|
|||||||
send_command(server_id, 'start_server');
|
send_command(server_id, 'start_server');
|
||||||
bootbox.alert({
|
bootbox.alert({
|
||||||
backdrop: true,
|
backdrop: true,
|
||||||
title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}',
|
title: '<span class="dynamicMsg">{% raw translate("dashboard", "sendingCommand", data["lang"]) %}</span>',
|
||||||
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("dashboard", "bePatientStart", data["lang"]) %} </div>'
|
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("dashboard", "bePatientStart", data["lang"]) %} </div>'
|
||||||
});
|
});
|
||||||
|
setTimeout(finishTimeout, 60000);
|
||||||
});
|
});
|
||||||
|
function finishTimeout() {
|
||||||
|
warn("It seems this is taking a while...it's possible you're using UBlock or a similar ad blocker and it's causing some of our connections to not make it to the server. Try disabling your ad blocker.",
|
||||||
|
null, 'wssError');
|
||||||
|
}
|
||||||
$(".stop_button").click(function () {
|
$(".stop_button").click(function () {
|
||||||
console.log("stopping server");
|
console.log("stopping server");
|
||||||
server_id = $(this).attr("data-id");
|
server_id = $(this).attr("data-id");
|
||||||
|
@ -1,69 +1,82 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
|
||||||
<!-- Required meta tags -->
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
||||||
<title>Crafty Controller</title>
|
|
||||||
<!-- plugins:css -->
|
|
||||||
<link rel="stylesheet" href="/static/assets/vendors/mdi/css/materialdesignicons.min.css">
|
|
||||||
<link rel="stylesheet" href="/static/assets/vendors/flag-icon-css/css/flag-icon.min.css">
|
|
||||||
<link rel="stylesheet" href="/static/assets/vendors/ti-icons/css/themify-icons.css">
|
|
||||||
<link rel="stylesheet" href="/static/assets/vendors/typicons/typicons.css">
|
|
||||||
<link rel="stylesheet" href="/static/assets/vendors/css/vendor.bundle.base.css">
|
|
||||||
<!-- endinject -->
|
|
||||||
<!-- Plugin css for this page -->
|
|
||||||
<!-- End Plugin css for this page -->
|
|
||||||
<!-- Layout styles -->
|
|
||||||
<link rel="stylesheet" href="/static/assets/css/dark/style.css">
|
|
||||||
<!-- End Layout styles -->
|
|
||||||
<link rel="shortcut icon" type="image/svg+xml" href="/static/assets/images/logo_small.svg">
|
|
||||||
<link rel="alternate icon" href="/static/assets/images/favicon.png" />
|
|
||||||
</head>
|
|
||||||
<body class="dark-theme">
|
|
||||||
<div class="container-scroller">
|
|
||||||
<div class="container-fluid page-body-wrapper full-page-wrapper">
|
|
||||||
<div class="content-wrapper d-flex align-items-center auth auth-bg-1 theme-one">
|
|
||||||
<div class="row w-100">
|
|
||||||
<div class="col-lg-4 mx-auto">
|
|
||||||
|
|
||||||
<div class="auto-form-wrapper">
|
<head>
|
||||||
<div class="text-center">
|
<!-- Required meta tags -->
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<title>Crafty Controller</title>
|
||||||
|
<!-- plugins:css -->
|
||||||
|
<link rel="stylesheet" href="/static/assets/vendors/mdi/css/materialdesignicons.min.css">
|
||||||
|
<link rel="stylesheet" href="/static/assets/vendors/flag-icon-css/css/flag-icon.min.css">
|
||||||
|
<link rel="stylesheet" href="/static/assets/vendors/ti-icons/css/themify-icons.css">
|
||||||
|
<link rel="stylesheet" href="/static/assets/vendors/typicons/typicons.css">
|
||||||
|
<link rel="stylesheet" href="/static/assets/vendors/css/vendor.bundle.base.css">
|
||||||
|
<!-- endinject -->
|
||||||
|
<!-- Plugin css for this page -->
|
||||||
|
<!-- End Plugin css for this page -->
|
||||||
|
<!-- Layout styles -->
|
||||||
|
<link rel="stylesheet" href="/static/assets/css/dark/style.css">
|
||||||
|
<!-- End Layout styles -->
|
||||||
|
<link rel="shortcut icon" type="image/svg+xml" href="/static/assets/images/logo_small.svg">
|
||||||
|
<link rel="alternate icon" href="/static/assets/images/favicon.png" />
|
||||||
|
</head>
|
||||||
|
<style>
|
||||||
|
.auth.auth-bg-1 {
|
||||||
|
background: url("../../static/assets/images/auth/{% raw data['background'] %}"),
|
||||||
|
url("../../static/assets/images/auth/login-1.jpg");
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<body class="dark-theme">
|
||||||
|
<div class="container-scroller">
|
||||||
|
<div class="container-fluid page-body-wrapper full-page-wrapper">
|
||||||
|
<div class="content-wrapper d-flex align-items-center auth auth-bg-1 theme-one">
|
||||||
|
<div class="row w-100">
|
||||||
|
<div class="col-lg-4 mx-auto">
|
||||||
|
|
||||||
|
<div class="auto-form-wrapper">
|
||||||
|
<div class="text-center">
|
||||||
<img src="/static/assets/images/logo_long.svg"><br /><br />
|
<img src="/static/assets/images/logo_long.svg"><br /><br />
|
||||||
<div class="col-sm-12 grid-margin stretch-card">
|
<div class="col-sm-12 grid-margin stretch-card">
|
||||||
<div class="card card-statistics social-card google-card card-colored">
|
<div class="card card-statistics social-card google-card card-colored">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="platform-name mb-3 mt-4 font-weight-semibold user-name">{{ translate('accessDenied', 'accessDenied', data['lang']) }}</h4>
|
<h4 class="platform-name mb-3 mt-4 font-weight-semibold user-name">{{ translate('accessDenied',
|
||||||
<h5 class="headline font-weight-medium">{{ translate('accessDenied', 'noAccess', data['lang']) }}</h5>
|
'accessDenied', data['lang']) }}</h4>
|
||||||
<p class="mb-2 comment font-weight-light">
|
<h5 class="headline font-weight-medium">{{ translate('accessDenied', 'noAccess', data['lang']) }}
|
||||||
{{ translate('accessDenied', 'contactAdmin', data['lang']) }}<br /><br />
|
</h5>
|
||||||
<a class="d-inline font-weight-medium" href="https://discord.gg/9VJPhCE"> {{ translate('accessDenied', 'contact', data['lang']) }}</a>
|
<p class="mb-2 comment font-weight-light">
|
||||||
</p>
|
{{ translate('accessDenied', 'contactAdmin', data['lang']) }}<br /><br />
|
||||||
|
<a class="d-inline font-weight-medium" href="https://discord.gg/9VJPhCE"> {{
|
||||||
|
translate('accessDenied', 'contact', data['lang']) }}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- content-wrapper ends -->
|
|
||||||
</div>
|
</div>
|
||||||
<!-- page-body-wrapper ends -->
|
<!-- content-wrapper ends -->
|
||||||
</div>
|
</div>
|
||||||
<!-- container-scroller -->
|
<!-- page-body-wrapper ends -->
|
||||||
<!-- plugins:js -->
|
</div>
|
||||||
<script src="/static/assets/vendors/js/vendor.bundle.base.js"></script>
|
<!-- container-scroller -->
|
||||||
<!-- endinject -->
|
<!-- plugins:js -->
|
||||||
<!-- inject:js -->
|
<script src="/static/assets/vendors/js/vendor.bundle.base.js"></script>
|
||||||
<script src="/static/assets/js/shared/off-canvas.js"></script>
|
<!-- endinject -->
|
||||||
<script src="/static/assets/js/shared/hoverable-collapse.js"></script>
|
<!-- inject:js -->
|
||||||
<script src="/static/assets/js/shared/misc.js"></script>
|
<script src="/static/assets/js/shared/off-canvas.js"></script>
|
||||||
<script src="/static/assets/js/shared/settings.js"></script>
|
<script src="/static/assets/js/shared/hoverable-collapse.js"></script>
|
||||||
<script src="/static/assets/js/shared/todolist.js"></script>
|
<script src="/static/assets/js/shared/misc.js"></script>
|
||||||
<!-- endinject -->
|
<script src="/static/assets/js/shared/settings.js"></script>
|
||||||
</body>
|
<script src="/static/assets/js/shared/todolist.js"></script>
|
||||||
|
<!-- endinject -->
|
||||||
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -8,16 +8,16 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
|
|
||||||
<!-- Page Title Header Starts-->
|
<!-- Page Title Header Starts-->
|
||||||
<div class="row page-title-header">
|
<div class="row page-title-header">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<!-- TODO: Translate the following -->
|
<h4 class="page-title">
|
||||||
<h4 class="page-title">{{ translate('panelConfig', 'pageTitle', data['lang']) }}</h4>
|
{{ translate('panelConfig', 'title', data['lang']) }}
|
||||||
|
<br />
|
||||||
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- Page Title Header Ends-->
|
<!-- Page Title Header Ends-->
|
||||||
|
|
||||||
@ -26,6 +26,23 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
|
|
||||||
|
{% if data['superuser'] %}
|
||||||
|
{% include "parts/crafty_config_list.html %}
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
<!-- Page Title Header Starts-->
|
||||||
|
<div class="row page-title-header">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="page-header">
|
||||||
|
<!-- TODO: Translate the following -->
|
||||||
|
<h4 class="page-title">{{ translate('panelConfig', 'pageTitle', data['lang']) }}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- Page Title Header Ends-->
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@ -33,9 +50,7 @@
|
|||||||
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('panelConfig', 'users', data['lang'])
|
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('panelConfig', 'users', data['lang'])
|
||||||
}}</h4>
|
}}</h4>
|
||||||
{% if data['user_data']['hints'] %}
|
{% if data['user_data']['hints'] %}
|
||||||
<span class="too_small" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}" ,
|
<span class="too_small" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}" , data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" , data-placement="top"></span>
|
||||||
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
|
|
||||||
data-placement="top"></span>
|
|
||||||
{% end %}
|
{% end %}
|
||||||
<!-- TODO: Translate the following -->
|
<!-- TODO: Translate the following -->
|
||||||
<div><a class="nav-link" href="/panel/add_user"><i class="fas fa-plus-circle"></i> {{
|
<div><a class="nav-link" href="/panel/add_user"><i class="fas fa-plus-circle"></i> {{
|
||||||
@ -133,9 +148,7 @@
|
|||||||
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'roles',
|
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'roles',
|
||||||
data['lang']) }}</h4>
|
data['lang']) }}</h4>
|
||||||
{% if data['user_data']['hints'] %}
|
{% if data['user_data']['hints'] %}
|
||||||
<span class="too_small2" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}" ,
|
<span class="too_small2" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}" , data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" , data-placement="top"></span>
|
||||||
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
|
|
||||||
data-placement="top"></span>
|
|
||||||
{% end %}
|
{% end %}
|
||||||
<div><a class="nav-link" href="/panel/add_role"><i class="fas fa-plus-circle"></i> {{
|
<div><a class="nav-link" href="/panel/add_role"><i class="fas fa-plus-circle"></i> {{
|
||||||
translate('panelConfig', 'newRole', data['lang']) }}</a></div>
|
translate('panelConfig', 'newRole', data['lang']) }}</a></div>
|
||||||
@ -221,74 +234,6 @@
|
|||||||
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'adminControls',
|
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'adminControls',
|
||||||
data['lang']) }}</h4>
|
data['lang']) }}</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
|
||||||
<button type="button" class="btn btn-outline-danger clear-comm">{{ translate('panelConfig',
|
|
||||||
'clearComms', data['lang']) }}</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-6 grid-margin stretch-card">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body">
|
|
||||||
|
|
||||||
<h4>{{ translate('panelConfig', 'loginImage', data['lang']) }}</h4>
|
|
||||||
<br />
|
|
||||||
<p class="card-description">
|
|
||||||
|
|
||||||
<form name="zip" method="post" class="server-wizard" onSubmit="wait_msg(true)">
|
|
||||||
{% raw xsrf_form_html() %}
|
|
||||||
<input type="hidden" value="import_zip" name="create_type">
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-12">
|
|
||||||
|
|
||||||
<div class="col-sm-12">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="server">{{ translate('panelConfig', 'backgroundUpload', data['lang'])
|
|
||||||
}}</label><br>
|
|
||||||
<span id="upload_input">
|
|
||||||
<input type="file" multiple="false" class="form-control" id="file" name="file" required
|
|
||||||
style="width: 70%;">
|
|
||||||
<button type="button" class="btn btn-info" id="upload-button" onclick="sendFile()"
|
|
||||||
disabled>UPLOAD</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 grid-margin stretch-card">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body">
|
|
||||||
<h4>{{ translate('panelConfig', 'loginBackground', data['lang']) }}</h4><br /><br><br />
|
|
||||||
<form id="photo_form">
|
|
||||||
<select class="form-select form-control form-control-lg select-css" id="photo" name="photo"
|
|
||||||
form="photo_form">
|
|
||||||
{% for image in data["backgrounds"] %}
|
|
||||||
<option value="{{image}}">{{image}}</option>
|
|
||||||
{% end %}
|
|
||||||
</select>
|
|
||||||
<div>
|
|
||||||
<br>
|
|
||||||
<h6>{{ translate('panelConfig', 'preview', data['lang']) }}:</h6>
|
|
||||||
<img style="width: 200px; height: 113px;"
|
|
||||||
src="../../static/assets/images/auth/{{ data['background'] }}">
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<button class="btn btn-outline-success select-photo" type="button">{{ translate('panelConfig',
|
|
||||||
'select', data['lang']) }}</button>
|
|
||||||
<button class="btn btn-outline-danger delete-photo" type="button">{{ translate('panelConfig',
|
|
||||||
'delete', data['lang']) }}</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -350,97 +295,5 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<script>
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
|
||||||
console.log('ready for JS!')
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
$(".show_button").click(function () {
|
|
||||||
console.log("showing key");
|
|
||||||
api_key = $(this).attr("data-id");
|
|
||||||
bootbox.alert({
|
|
||||||
backdrop: true,
|
|
||||||
title: '',
|
|
||||||
message: api_key,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.clear-comm').click(function () {
|
|
||||||
var token = getCookie("_xsrf")
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
headers: { 'X-XSRFToken': token },
|
|
||||||
url: '/ajax/clear_comm',
|
|
||||||
success: function (data) {
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
$('.delete-photo').click(function () {
|
|
||||||
var token = getCookie("_xsrf")
|
|
||||||
let photo = $('#photo').find(":selected").val();
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
headers: { 'X-XSRFToken': token },
|
|
||||||
url: '/ajax/delete_photo?photo=' + photo,
|
|
||||||
success: function (data) {
|
|
||||||
location.reload();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
$('.select-photo').click(function () {
|
|
||||||
var token = getCookie("_xsrf")
|
|
||||||
let photo = $('#photo').find(":selected").val();
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
headers: { 'X-XSRFToken': token },
|
|
||||||
url: '/ajax/select_photo?photo=' + photo,
|
|
||||||
success: function (data) {
|
|
||||||
window.location.reload();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
var file;
|
|
||||||
function sendFile() {
|
|
||||||
file = $("#file")[0].files[0]
|
|
||||||
document.getElementById("upload_input").innerHTML = '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"> <i class="fa-solid fa-spinner"></i></div></div>'
|
|
||||||
let xmlHttpRequest = new XMLHttpRequest();
|
|
||||||
let token = getCookie("_xsrf")
|
|
||||||
let fileName = file.name
|
|
||||||
let target = '/upload'
|
|
||||||
let mimeType = file.type
|
|
||||||
let size = file.size
|
|
||||||
let type = 'background'
|
|
||||||
|
|
||||||
xmlHttpRequest.open('POST', target, true);
|
|
||||||
xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType);
|
|
||||||
xmlHttpRequest.setRequestHeader('X-XSRFToken', token);
|
|
||||||
xmlHttpRequest.setRequestHeader('X-Content-Length', size);
|
|
||||||
xmlHttpRequest.setRequestHeader('X-Content-Disposition', 'attachment; filename="' + fileName + '"');
|
|
||||||
xmlHttpRequest.setRequestHeader('X-Content-Upload-Type', type);
|
|
||||||
xmlHttpRequest.setRequestHeader('X-FileName', fileName);
|
|
||||||
xmlHttpRequest.addEventListener('load', (event) => {
|
|
||||||
if (event.target.responseText == 'success') {
|
|
||||||
console.log('Upload for file', file.name, 'was successful!')
|
|
||||||
document.getElementById("upload_input").innerHTML = '<div class="card-header header-sm d-flex justify-content-between align-items-center"><span id="file-uploaded" style="color: gray;">' + fileName + '</span> 🔒</div>';
|
|
||||||
setTimeout(function () {
|
|
||||||
window.location.reload();
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
alert('Upload failed with response: ' + event.target.responseText);
|
|
||||||
doUpload = false;
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
xmlHttpRequest.addEventListener('error', (e) => {
|
|
||||||
console.error('Error while uploading file', file.name + '.', 'Event:', e)
|
|
||||||
}, false);
|
|
||||||
xmlHttpRequest.send(file);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
14
app/frontend/templates/panel/parts/crafty_config_list.html
Normal file
14
app/frontend/templates/panel/parts/crafty_config_list.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<ul class="nav nav-tabs col-md-12 tab-simple-styled" role="tablist" style="margin-top: 0;">
|
||||||
|
<li class="nav-item term-nav-item">
|
||||||
|
<a class="nav-link {% if data['active_link'] == 'panel_config' %}active{% end %}" href="/panel/panel_config" role="tab" aria-selected="false">
|
||||||
|
<i class="fas fa-file-signature"></i>Panel Config</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item term-nav-item">
|
||||||
|
<a class="nav-link {% if data['active_link'] == 'config_json' %}active{% end %}" href="/panel/config_json" role="tab" aria-selected="false">
|
||||||
|
<i class="fas fa-file-signature"></i>Config.json</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item term-nav-item">
|
||||||
|
<a class="nav-link {% if data['active_link'] == 'custom_login' %}active{% end %}" href="/panel/custom_login" role="tab" aria-selected="false">
|
||||||
|
<i class="fas fa-file-signature"></i>Custom Login</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
@ -107,6 +107,40 @@
|
|||||||
translate('serverBackups', 'shutdown', data['lang']) }}
|
translate('serverBackups', 'shutdown', data['lang']) }}
|
||||||
{% end %}
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="command-check" class="form-check-label ml-4 mb-4"></label>
|
||||||
|
{% if data['backup_config']['before'] %}
|
||||||
|
<input type="checkbox" class="form-check-input" id="before-check" name="before-check" checked>Run
|
||||||
|
Command Before Backup
|
||||||
|
<br>
|
||||||
|
<input type="text" class="form-control" name="backup_before" id="backup_before"
|
||||||
|
value="{{ data['backup_config']['before'] }}" placeholder="We enter the / for you"
|
||||||
|
style="display: inline-block;">
|
||||||
|
{% else %}
|
||||||
|
<input type="checkbox" class="form-check-input" id="before-check" name="before-check">Run Command
|
||||||
|
Before Backup
|
||||||
|
<br>
|
||||||
|
<input type="text" class="form-control" name="backup_before" id="backup_before" value=""
|
||||||
|
placeholder="We enter the / for you." style="display: none;">
|
||||||
|
{% end %}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="command-check" class="form-check-label ml-4 mb-4"></label>
|
||||||
|
{% if data['backup_config']['after'] %}
|
||||||
|
<input type="checkbox" class="form-check-input" id="after-check" name="after-check" checked>Run
|
||||||
|
Command After Backup
|
||||||
|
<br>
|
||||||
|
<input type="text" class="form-control" name="backup_after" id="backup_after"
|
||||||
|
value="{{ data['backup_config']['after'] }}" placeholder="We enter the / for you"
|
||||||
|
style="display: inline-block;">
|
||||||
|
{% else %}
|
||||||
|
<input type="checkbox" class="form-check-input" id="after-check" name="after-check">Run Command
|
||||||
|
Before Backup
|
||||||
|
<br>
|
||||||
|
<input type="text" class="form-control" name="backup_after" id="backup_after" value=""
|
||||||
|
placeholder="We enter the / for you." style="display: none;">
|
||||||
|
{% end %}
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="server">{{ translate('serverBackups', 'exclusionsTitle', data['lang']) }} <small> - {{
|
<label for="server">{{ translate('serverBackups', 'exclusionsTitle', data['lang']) }} <small> - {{
|
||||||
translate('serverBackups', 'excludedChoose', data['lang']) }}</small></label>
|
translate('serverBackups', 'excludedChoose', data['lang']) }}</small></label>
|
||||||
@ -344,6 +378,22 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$("#before-check").on("click", function () {
|
||||||
|
if ($("#before-check:checked").val()) {
|
||||||
|
$("#backup_before").css("display", "inline-block");
|
||||||
|
} else {
|
||||||
|
$("#backup_before").css("display", "none");
|
||||||
|
$("#backup_before").val("");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("#after-check").on("click", function () {
|
||||||
|
if ($("#after-check:checked").val()) {
|
||||||
|
$("#backup_after").css("display", "inline-block");
|
||||||
|
} else {
|
||||||
|
$("#backup_after").css("display", "none");
|
||||||
|
$("#backup_after").val("");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
try {
|
try {
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<style>
|
<style>
|
||||||
.auth.auth-bg-1 {
|
.auth.auth-bg-1 {
|
||||||
background: url("../../static/assets/images/auth/{{data['background']}}"),
|
background: url("../../static/assets/images/auth/{% raw data['background'] %}"),
|
||||||
url("../../static/assets/images/auth/login-1.jpg");
|
url("../../static/assets/images/auth/login-1.jpg");
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<style>
|
<style>
|
||||||
.auth.auth-bg-1 {
|
.auth.auth-bg-1 {
|
||||||
background: url("../../static/assets/images/auth/{{data['background']}}"),
|
background: url("../../static/assets/images/auth/{% raw data['background'] %}"),
|
||||||
url("../../static/assets/images/auth/login-1.jpg");
|
url("../../static/assets/images/auth/login-1.jpg");
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,10 @@
|
|||||||
</head>
|
</head>
|
||||||
<style>
|
<style>
|
||||||
.auth.auth-bg-1 {
|
.auth.auth-bg-1 {
|
||||||
background: url("../../static/assets/images/auth/{{data['background']}}"),
|
background: url("../../static/assets/images/auth/{% raw data['background'] %}"),
|
||||||
url("../../static/assets/images/auth/login-1.jpg");
|
url("../../static/assets/images/auth/login-1.jpg");
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@ -36,8 +37,9 @@
|
|||||||
<div class="row w-100">
|
<div class="row w-100">
|
||||||
<div class="col-lg-4 mx-auto">
|
<div class="col-lg-4 mx-auto">
|
||||||
|
|
||||||
<div class="auto-form-wrapper login-modal">
|
<div id="login-form-background" class="auto-form-wrapper login-modal">
|
||||||
<div class="text-center">
|
<div id="login_opacity" data-value="{{ data['login_opacity'] }}" hidden></div>
|
||||||
|
<div class="text-center auto-form-logo">
|
||||||
<img src="/static/assets/images/logo_long.svg">
|
<img src="/static/assets/images/logo_long.svg">
|
||||||
</div>
|
</div>
|
||||||
<style>
|
<style>
|
||||||
@ -69,25 +71,25 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
{% if data['query'] %}
|
{% if data['query'] %}
|
||||||
<form action="/public/login?{{ data['query'] }}" method="post">
|
<form action="/login?{{ data['query'] }}" method="post">
|
||||||
{% else %}
|
{% else %}
|
||||||
<form action="/public/login" method="post">
|
<form action="/login" method="post">
|
||||||
{% end %}
|
{% end %}
|
||||||
{% raw xsrf_form_html() %}
|
{% raw xsrf_form_html() %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="label">{{ translate('login', 'username', data['lang']) }}</label>
|
<label class="label">{{ translate('login', 'username', data['lang']) }}</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="form-control login-text-input login-input"
|
<input type="text" class="form-control login-text-input login-input"
|
||||||
placeholder="{{ translate('login', 'username', data['lang']) }}" name="username" id="username"
|
placeholder="{{ translate('login', 'username', data['lang']) }}" name="username" id="username"
|
||||||
required="true">
|
required="true">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="label">{{ translate('login', 'password', data['lang']) }}</label>
|
<label class="label">{{ translate('login', 'password', data['lang']) }}</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="password" class="form-control login-text-input login-input"
|
<input type="password" class="form-control login-text-input login-input"
|
||||||
placeholder="{{ translate('login', 'password', data['lang']) }}" name="password" id="password"
|
placeholder="{{ translate('login', 'password', data['lang']) }}" name="password" id="password"
|
||||||
required="true">
|
required="true">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -133,6 +135,13 @@
|
|||||||
<script src="/static/assets/js/shared/settings.js"></script>
|
<script src="/static/assets/js/shared/settings.js"></script>
|
||||||
<script src="/static/assets/js/shared/todolist.js"></script>
|
<script src="/static/assets/js/shared/todolist.js"></script>
|
||||||
<!-- endinject -->
|
<!-- endinject -->
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
let login_opacity_div = document.getElementById('login_opacity');
|
||||||
|
let opacity = login_opacity_div.getAttribute('data-value');
|
||||||
|
document.getElementById('login-form-background').style.background = 'rgb(34, 36, 55, ' + (opacity / 100) + ')';
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -9,7 +9,7 @@
|
|||||||
<!-- View for Large screen -->
|
<!-- View for Large screen -->
|
||||||
<style>
|
<style>
|
||||||
.auth.auth-bg-1 {
|
.auth.auth-bg-1 {
|
||||||
background: url("../../static/assets/images/auth/{{data['background']}}"),
|
background: url("../../static/assets/images/auth/{% raw data['background'] %}"),
|
||||||
url("../../static/assets/images/auth/login-1.jpg");
|
url("../../static/assets/images/auth/login-1.jpg");
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
@ -5,61 +5,61 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="auto-form-wrapper">
|
<div class="auto-form-wrapper">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<!-- <img src="/static/assets/images/logo_long.svg">-->
|
<!-- <img src="/static/assets/images/logo_long.svg">-->
|
||||||
{{ _('Configure Your Existing Server') }}<br /><br />
|
{{ _('Configure Your Existing Server') }}<br /><br />
|
||||||
|
</div>
|
||||||
|
<form action="/login" method="post">
|
||||||
|
{% raw xsrf_form_html() %}
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="label">
|
||||||
|
{{ _('Server Name') }} - <small>{{ _('Example Survival Server') }}</small>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control" placeholder="{{ _('Server Name') }}" name="server_name" value="{{_('MyFirstServer') }}" maxlength="55">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form action="/public/login" method="post">
|
|
||||||
{% raw xsrf_form_html() %}
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="label">
|
{% if data['is_windows'] %}
|
||||||
{{ _('Server Name') }} - <small>{{ _('Example Survival Server') }}</small>
|
<label class="label">
|
||||||
</label>
|
{{ _('Server Path') }} - <small>{{ _('Example c:\minecraft\server') }}</small>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="form-control" placeholder="{{ _('Server Name') }}" name="server_name" value="{{_('MyFirstServer') }}" maxlength="55">
|
<input type="text" class="form-control" placeholder="{{ _('Server Path') }}" name="server_path"
|
||||||
</div>
|
value="c:\windows\minecraft" maxlength="255">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
{% else %}
|
||||||
{% if data['is_windows'] %}
|
|
||||||
<label class="label">
|
|
||||||
{{ _('Server Path') }} - <small>{{ _('Example c:\minecraft\server') }}</small>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div class="input-group">
|
<label class="label">
|
||||||
<input type="text" class="form-control" placeholder="{{ _('Server Path') }}" name="server_path"
|
{{ _('Server Path') }} - <small>{{ _("Example: /var/opt/minecraft/server") }}</small>
|
||||||
value="c:\windows\minecraft" maxlength="255">
|
</label>
|
||||||
</div>
|
|
||||||
|
|
||||||
{% else %}
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control" placeholder="{{ _('Server Path') }}" name="server_path"
|
||||||
|
value="c:\windows\minecraft" maxlength="255">
|
||||||
|
</div>
|
||||||
|
{% end %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<label class="label">
|
<div class="form-group">
|
||||||
{{ _('Server Path') }} - <small>{{ _("Example: /var/opt/minecraft/server") }}</small>
|
<label class="label">
|
||||||
</label>
|
{{ _('Server Jar') }} - <small>{{ _('Example paper.jar') }}</small>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div class="input-group">
|
<input type="text" class="form-control" placeholder="{{ _('Server Jar') }}" name="server_jar" value="paper.jar" maxlength="255">
|
||||||
<input type="text" class="form-control" placeholder="{{ _('Server Path') }}" name="server_path"
|
</div>
|
||||||
value="c:\windows\minecraft" maxlength="255">
|
|
||||||
</div>
|
|
||||||
{% end %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="label">
|
|
||||||
{{ _('Server Jar') }} - <small>{{ _('Example paper.jar') }}</small>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<input type="text" class="form-control" placeholder="{{ _('Server Jar') }}" name="server_jar" value="paper.jar" maxlength="255">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button class="btn btn-primary submit-btn btn-block"><i class="fas fa-save"></i> Save</button>
|
<button class="btn btn-primary submit-btn btn-block"><i class="fas fa-save"></i> Save</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
18
app/migrations/20230111_adding_login_opacity.py
Normal file
18
app/migrations/20230111_adding_login_opacity.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by database migrator
|
||||||
|
import peewee
|
||||||
|
|
||||||
|
|
||||||
|
def migrate(migrator, database, **kwargs):
|
||||||
|
migrator.add_columns(
|
||||||
|
"crafty_settings", login_opacity=peewee.IntegerField(default=100)
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
Write your migrations here.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def rollback(migrator, database, **kwargs):
|
||||||
|
migrator.drop_columns("crafty_settings", ["login_opacity"])
|
||||||
|
"""
|
||||||
|
Write your rollback migrations here.
|
||||||
|
"""
|
35
app/migrations/20230119_command_drop.py
Normal file
35
app/migrations/20230119_command_drop.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Generated by database migrator
|
||||||
|
import datetime
|
||||||
|
from peewee import *
|
||||||
|
from app.classes.models.users import Users
|
||||||
|
from app.classes.models.servers import Servers
|
||||||
|
|
||||||
|
|
||||||
|
def migrate(migrator, database, **kwargs):
|
||||||
|
migrator.drop_table("commands")
|
||||||
|
"""
|
||||||
|
Write your migrations here.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def rollback(migrator, database, **kwargs):
|
||||||
|
db = database
|
||||||
|
|
||||||
|
class Commands(Model):
|
||||||
|
command_id = AutoField()
|
||||||
|
created = DateTimeField(default=datetime.datetime.now)
|
||||||
|
server_id = ForeignKeyField(Servers, backref="server", index=True)
|
||||||
|
user = ForeignKeyField(Users, backref="user", index=True)
|
||||||
|
source_ip = CharField(default="127.0.0.1")
|
||||||
|
command = CharField(default="")
|
||||||
|
executed = BooleanField(default=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
table_name = "commands"
|
||||||
|
database = db
|
||||||
|
|
||||||
|
migrator.create_table(Commands)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Write your rollback migrations here.
|
||||||
|
"""
|
18
app/migrations/20230120_backup_command.py
Normal file
18
app/migrations/20230120_backup_command.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by database migrator
|
||||||
|
import peewee
|
||||||
|
|
||||||
|
|
||||||
|
def migrate(migrator, database, **kwargs):
|
||||||
|
migrator.add_columns("backups", before=peewee.CharField(default=""))
|
||||||
|
migrator.add_columns("backups", after=peewee.CharField(default=""))
|
||||||
|
"""
|
||||||
|
Write your migrations here.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def rollback(migrator, database, **kwargs):
|
||||||
|
migrator.drop_columns("backups", ["before"])
|
||||||
|
migrator.drop_columns("backups", ["after"])
|
||||||
|
"""
|
||||||
|
Write your rollback migrations here.
|
||||||
|
"""
|
606
app/translations/cs_CS.json
Normal file
606
app/translations/cs_CS.json
Normal file
@ -0,0 +1,606 @@
|
|||||||
|
{
|
||||||
|
"404": {
|
||||||
|
"contact": "Kontaktujte podporu Crafty přes Discord",
|
||||||
|
"notFound": "Stránka nebyla nalezena",
|
||||||
|
"unableToFind": "Hledanou stránku se nám nepodařilo najít. Zkuste to prosím znovu nebo se vraťte a obnovte stránku."
|
||||||
|
},
|
||||||
|
"accessDenied": {
|
||||||
|
"accessDenied": "Přístup odepřen",
|
||||||
|
"contact": "Kontaktujte podporu Crafty přes Discord",
|
||||||
|
"contactAdmin": "Pro přístup k tomuto prostředku se obraťte na správce serveru, nebo pokud si myslíte, že byste k němu měli mít přístup, kontaktujte podporu.",
|
||||||
|
"noAccess": "K tomuto zdroji nemáte přístup"
|
||||||
|
},
|
||||||
|
"apiKeys": {
|
||||||
|
"apiKeys": "Klíče API",
|
||||||
|
"auth": "Autorizován? ",
|
||||||
|
"buttons": "Tlačítka",
|
||||||
|
"config": "Nastavení",
|
||||||
|
"crafty": "Crafty: ",
|
||||||
|
"created": "Vytvořen",
|
||||||
|
"createNew": "Vytvořit nový token API",
|
||||||
|
"deleteKeyConfirmation": "Chcete tento API klíč odstranit? Tuto akci nelze vrátit zpět.",
|
||||||
|
"deleteKeyConfirmationTitle": "Odstranit klíč API ${keyId}?",
|
||||||
|
"getToken": "Získat token",
|
||||||
|
"name": "Jméno",
|
||||||
|
"nameDesc": "Jak chcete nazvat tento token API? ",
|
||||||
|
"no": "Ne",
|
||||||
|
"pageTitle": "Úprava uživatelských klíčů API",
|
||||||
|
"permName": "Název oprávnění",
|
||||||
|
"perms": "Oprávnění",
|
||||||
|
"server": "Server: ",
|
||||||
|
"superUser": "Super uživatel",
|
||||||
|
"yes": "Ano"
|
||||||
|
},
|
||||||
|
"base": {
|
||||||
|
"doesNotWorkWithoutJavascript": "<strong>Varování: </strong>Crafty nefunguje správně, pokud není povolen JavaScript!"
|
||||||
|
},
|
||||||
|
"credits": {
|
||||||
|
"developmentTeam": "Vývojový tým",
|
||||||
|
"hugeDesc": "Neskutečně",
|
||||||
|
"pageDescription": "Bez těchto lidí bychom neměli Crafty.",
|
||||||
|
"pageTitle": "Zásluhy",
|
||||||
|
"patreonDesc": "našim příznivcům Patreonu / Ko-fi!",
|
||||||
|
"patreonOther": "Další",
|
||||||
|
"patreonSupporter": "Podpůrci Patreonu / Ko-fi",
|
||||||
|
"patreonUpdate": "Poslední aktualizace:",
|
||||||
|
"retiredStaff": "Bývalí zaměstnanci",
|
||||||
|
"subscriberName": "Jméno",
|
||||||
|
"subscriptionLevel": "Úroveň",
|
||||||
|
"supportTeam": "Tým podpory a dokumentace",
|
||||||
|
"thankYou": "DĚKUJEME",
|
||||||
|
"translationDesc": "naší komunitě, která překládá! [ Aktivní = 🟢 Neaktivní/ukončený = ⚪ ]",
|
||||||
|
"translationName": "Jazyk",
|
||||||
|
"translationTitle": "Překlad do jazyků",
|
||||||
|
"translator": "Překladatelé"
|
||||||
|
},
|
||||||
|
"dashboard": {
|
||||||
|
"actions": "Akce",
|
||||||
|
"allServers": "Všechny servery",
|
||||||
|
"avg": "Průměr",
|
||||||
|
"backups": "Zálohy",
|
||||||
|
"bePatientClone": "Buďte prosím trpěliví, než se dokončí klonování serveru.<br /> Tato obrazovka se za okamžik aktualizuje",
|
||||||
|
"bePatientRestart": "Buďte prosím trpěliví, než se dokončí restart serveru.<br /> Tato obrazovka se za okamžik aktualizuje",
|
||||||
|
"bePatientStart": "Buďte prosím trpěliví, než se dokončí spuštění serveru.<br /> Tato obrazovka se za okamžik aktualizuje",
|
||||||
|
"bePatientStop": "Buďte prosím trpěliví, než se dokončí zastavení serveru.<br /> Tato obrazovka se za okamžik aktualizuje",
|
||||||
|
"cannotSee": "Nezobrazuje se vám vše?",
|
||||||
|
"cannotSeeOnMobile": "Nezobrazuje se vám vše na mobilu?",
|
||||||
|
"cannotSeeOnMobile2": "Zkuste posunout tabulku do strany.",
|
||||||
|
"clone": "Klon",
|
||||||
|
"cloneConfirm": "Opravdu chcete tento server naklonovat? Tento proces může chvíli trvat.",
|
||||||
|
"cpuCores": "Jádra CPU",
|
||||||
|
"cpuCurFreq": "Aktuální takt CPU",
|
||||||
|
"cpuMaxFreq": "Maximální takt CPU",
|
||||||
|
"cpuUsage": "Využití CPU",
|
||||||
|
"crashed": "Crashnuté",
|
||||||
|
"dashboard": "Ovládací panel",
|
||||||
|
"delay-explained": "Služba/agent byla nedávno spuštěna a zpožďuje spuštění instance minecraft serveru.",
|
||||||
|
"host": "Hostitel",
|
||||||
|
"kill": "Zabít proces",
|
||||||
|
"killing": "Zabíjím proces...",
|
||||||
|
"lastBackup": "Poslední:",
|
||||||
|
"max": "Max",
|
||||||
|
"memUsage": "Využití paměti",
|
||||||
|
"motd": "MOTD",
|
||||||
|
"newServer": "Vytvořit nový server",
|
||||||
|
"nextBackup": "Další:",
|
||||||
|
"no-servers": "V současné době nejsou k dispozici žádné servery. Chcete-li začít, klikněte na",
|
||||||
|
"offline": "Offline",
|
||||||
|
"online": "Online",
|
||||||
|
"players": "Hráči",
|
||||||
|
"restart": "Restartovat",
|
||||||
|
"sendingCommand": "Odeslání příkazu",
|
||||||
|
"server": "Server",
|
||||||
|
"servers": "Servery",
|
||||||
|
"size": "Velikost složky serveru",
|
||||||
|
"start": "Start",
|
||||||
|
"starting": "Zpožděný start",
|
||||||
|
"status": "Stav",
|
||||||
|
"stop": "Zastavit",
|
||||||
|
"version": "Verze",
|
||||||
|
"welcome": "Vítejte v Crafty Controlleru",
|
||||||
|
"installing": "Instalace..."
|
||||||
|
},
|
||||||
|
"datatables": {
|
||||||
|
"i18n": {
|
||||||
|
"aria": {
|
||||||
|
"sortAscending": ": aktivace řazení sloupce vzestupně",
|
||||||
|
"sortDescending": ": aktivace seřazení sloupce sestupně"
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"collection": "Sbírka <span class='ui-button-icon-primary ui-icon ui-icon-triangle-1-s'/>",
|
||||||
|
"colvis": "Viditelnost sloupců",
|
||||||
|
"colvisRestore": "Obnovit viditelnost",
|
||||||
|
"copy": "Kopírovat",
|
||||||
|
"copyKeys": "Stisknutím kláves Ctrl nebo u2318 + C zkopírujete data tabulky do systémové schránky.<br><br>Chcete-li tuto zprávu zrušit, klikněte na ni nebo stiskněte klávesu ESC.",
|
||||||
|
"copySuccess": {
|
||||||
|
"1": "Zkopírován 1 řádek do schránky",
|
||||||
|
"2": "Zkopírovány 2 řádky do schránky",
|
||||||
|
"3": "Zkopírovány 3 řádky do schránky",
|
||||||
|
"4": "Zkopírovány 4 řádky do schránky",
|
||||||
|
"_": "Zkopírováno %d řádků do schránky"
|
||||||
|
},
|
||||||
|
"copyTitle": "Zkopírovat do schránky",
|
||||||
|
"csv": "CSV",
|
||||||
|
"excel": "Excel",
|
||||||
|
"pageLength": {
|
||||||
|
"1": "Zobrazit 1 řádek",
|
||||||
|
"2": "Zobrazit 2 řádky",
|
||||||
|
"3": "Zobrazit 3 řádky",
|
||||||
|
"4": "Zobrazit 4 řádky",
|
||||||
|
"-1": "Zobrazit všechny řádky",
|
||||||
|
"_": "Zobrazit %d řádků"
|
||||||
|
},
|
||||||
|
"pdf": "PDF",
|
||||||
|
"print": "Tisk"
|
||||||
|
},
|
||||||
|
"decimal": "",
|
||||||
|
"emptyTable": "V tabulce nejsou k dispozici žádné údaje",
|
||||||
|
"info": "Zobrazeno _START_ až _END_ z _TOTAL_ záznamů",
|
||||||
|
"infoEmpty": "Zobrazeno 0 až 0 z 0 záznamů",
|
||||||
|
"infoFiltered": "(filtrováno z _MAX_ celkových záznamů)",
|
||||||
|
"infoPostFix": "",
|
||||||
|
"lengthMenu": "Zobrazit položky _MENU_",
|
||||||
|
"loadingRecords": "Načítání...",
|
||||||
|
"paginate": {
|
||||||
|
"first": "První",
|
||||||
|
"last": "Poslední",
|
||||||
|
"next": "Další",
|
||||||
|
"previous": "Předchozí"
|
||||||
|
},
|
||||||
|
"processing": "Zpracování...",
|
||||||
|
"search": "Hledat:",
|
||||||
|
"select": {
|
||||||
|
"cells": {
|
||||||
|
"0": "Kliknutím na buňku ji vyberete",
|
||||||
|
"1": "Vybraná %d buňka",
|
||||||
|
"2": "Vybrané %d buňky",
|
||||||
|
"3": "Vybrané %d buňky",
|
||||||
|
"4": "Vybrané %d buňky",
|
||||||
|
"_": "Vybráno %d buněk"
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"0": "Kliknutím na sloupec jej vyberete",
|
||||||
|
"1": "Vybraný %d sloupec",
|
||||||
|
"2": "Vybrané %d sloupce",
|
||||||
|
"3": "Vybrané %d sloupce",
|
||||||
|
"4": "Vybrané %d sloupce",
|
||||||
|
"_": "Vybráno %d sloupců"
|
||||||
|
},
|
||||||
|
"rows": {
|
||||||
|
"0": "Kliknutím na řádek jej vyberete",
|
||||||
|
"1": "Vybraný %d řádek",
|
||||||
|
"2": "Vybrané %d řádky",
|
||||||
|
"3": "Vybrané %d řádky",
|
||||||
|
"4": "Vybrané %d řádky",
|
||||||
|
"_": "Vybráno %d řádků"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"thousands": " ",
|
||||||
|
"zeroRecords": "Nebyly nalezeny žádné odpovídající záznamy"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"contact": "Kontaktujte podporu Crafty přes Discord",
|
||||||
|
"embarassing": "Ach jo, no, to je trapné.",
|
||||||
|
"error": "Chyba!",
|
||||||
|
"eulaAgree": "Souhlasíte?",
|
||||||
|
"eulaMsg": "Musíte souhlasit s ",
|
||||||
|
"privMsg": "a ",
|
||||||
|
"eulaTitle": "Souhlas s EULA",
|
||||||
|
"agree": "Souhlasím",
|
||||||
|
"cancel": "Zrušit",
|
||||||
|
"fileTooLarge": "Odeslání se nezdařilo. Příliš velký nahraný soubor. Obraťte se na správce systému.",
|
||||||
|
"hereIsTheError": "Zde je chyba",
|
||||||
|
"internet": "Zjistili jsme, že počítač se spuštěným programem Crafty není připojen k internetu. Připojení klientů k serveru může být omezeno.",
|
||||||
|
"no-file": "Zdá se, že nemůžeme najít požadovaný soubor. Překontrolujte cestu. Má Crafty správné oprávnění?",
|
||||||
|
"noJava": "Server {} se nepodařilo spustit s kódem chyby: Zjistili jsme, že Java není nainstalována. Nainstalujte prosím Javu a poté spusťte server.",
|
||||||
|
"not-downloaded": "Zdá se, že nemůžeme najít váš spustitelný soubor. Bylo jeho stahování dokončeno? Jsou oprávnění nastavena na spustitelný soubor?",
|
||||||
|
"portReminder": "Zjistili jsme, že server {} byl spuštěn poprvé. Ujistěte se, že jste přesměrovali port {} přes váš směrovač/firewall, aby byl tento port vzdáleně přístupný z internetu.",
|
||||||
|
"start-error": "Server {} se nepodařilo spustit s kódem chyby: {}",
|
||||||
|
"terribleFailure": "Jaké strašné selhání!",
|
||||||
|
"superError": "Pro dokončení této akce musíte být super uživatel.",
|
||||||
|
"fileError": "Typ souboru musí být obrázek."
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"allRightsReserved": "Všechna práva vyhrazena",
|
||||||
|
"copyright": "Autorská práva",
|
||||||
|
"version": "Verze"
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"forgotPassword": "Zapomenuté heslo",
|
||||||
|
"login": "Přihlásit se",
|
||||||
|
"password": "Heslo",
|
||||||
|
"username": "Uživatelské jméno"
|
||||||
|
},
|
||||||
|
"notify": {
|
||||||
|
"activityLog": "Záznamy o činnosti",
|
||||||
|
"backupComplete": "Zálohování serveru {} bylo úspěšně dokončeno",
|
||||||
|
"backupStarted": "Bylo spuštěno zálohování serveru {}",
|
||||||
|
"downloadLogs": "Stáhnout protokoly podpory?",
|
||||||
|
"finishedPreparing": "Dokončili jsme přípravu protokolů podpory. Klikněte na tlačítko Stáhnout pro stažení",
|
||||||
|
"logout": "Odhlásit se",
|
||||||
|
"preparingLogs": " Počkejte prosím, než připravíme vaše protokoly... Až budou připraveny, pošleme vám oznámení. U rozsáhlých zavádění to může chvíli trvat.",
|
||||||
|
"supportLogs": "Protokoly podpory"
|
||||||
|
},
|
||||||
|
"panelConfig": {
|
||||||
|
"adminControls": "Ovládání správce",
|
||||||
|
"allowedServers": "Povolené servery",
|
||||||
|
"assignedRoles": "Přidělené role",
|
||||||
|
"cancel": "Zrušit",
|
||||||
|
"clearComms": "Vymazat nevykonané příkazy",
|
||||||
|
"delete": "Smazat",
|
||||||
|
"edit": "Upravit",
|
||||||
|
"enabled": "Zapnuto",
|
||||||
|
"match": "Hesla musí být stejná",
|
||||||
|
"newRole": "Přidat novou roli",
|
||||||
|
"newUser": "Přidat nového uživatele",
|
||||||
|
"pageTitle": "Nastavení panelu",
|
||||||
|
"role": "Role",
|
||||||
|
"roles": "Role",
|
||||||
|
"roleUsers": "Uživatelé s rolí",
|
||||||
|
"save": "Uložit",
|
||||||
|
"superConfirm": "Postupujte pouze v případě, že chcete, aby měl tento uživatel přístup ke VŠEM (ke všem uživatelským účtům, serverům, nastavení panelu atd.). Může vám dokonce odebrat práva superuživatele.",
|
||||||
|
"superConfirmTitle": "Povolit superuživatele? Jste si jisti?",
|
||||||
|
"user": "Uživatel",
|
||||||
|
"users": "Uživatelé",
|
||||||
|
"loginImage": "Nahrajte obrázek na pozadí přihlašovací obrazovky.",
|
||||||
|
"backgroundUpload": "Nahrání pozadí",
|
||||||
|
"loginBackground": "Přihlašovací obrázek na pozadí",
|
||||||
|
"select": "Vyberte",
|
||||||
|
"selectImage": "Vyberte obrázek",
|
||||||
|
"preview": "Náhled"
|
||||||
|
},
|
||||||
|
"rolesConfig": {
|
||||||
|
"config": "Nastavení role",
|
||||||
|
"configDesc": "Zde můžete změnit nastavení své role.",
|
||||||
|
"configUpdate": "Poslední aktualizace: ",
|
||||||
|
"created": "Vytvořený: ",
|
||||||
|
"delRole": "Smazat roli",
|
||||||
|
"doesNotExist": "Nemůžete odstranit něco, co ještě neexistuje",
|
||||||
|
"pageTitle": "Upravit roli",
|
||||||
|
"pageTitleNew": "Nová role",
|
||||||
|
"permAccess": "Přístup?",
|
||||||
|
"permName": "Název oprávnění",
|
||||||
|
"permsServer": "Oprávnění, která má tato role pro tyto servery",
|
||||||
|
"roleConfigArea": "Oblast nastavení role",
|
||||||
|
"roleDesc": "Jak byste chtěl aby se tato role jmenovala?",
|
||||||
|
"roleName": "Název role: ",
|
||||||
|
"rolePerms": "Oprávnění role",
|
||||||
|
"roleServers": "Povolené servery",
|
||||||
|
"roleTitle": "Nastavení rolí",
|
||||||
|
"roleUserName": "Uživatelské jméno",
|
||||||
|
"roleUsers": "Uživatelé role: ",
|
||||||
|
"serverAccess": "Přístup?",
|
||||||
|
"serverName": "Název serveru",
|
||||||
|
"serversDesc": "servery, ke kterým má tato role přístup",
|
||||||
|
"selectManager": "Výběr manažera pro tuto roli"
|
||||||
|
},
|
||||||
|
"serverBackups": {
|
||||||
|
"backupAtMidnight": "Automatické zálohování o půlnoci?",
|
||||||
|
"backupNow": "Zálohovat nyní!",
|
||||||
|
"backupTask": "Bylo spuštěno zálohování.",
|
||||||
|
"cancel": "Zrušit",
|
||||||
|
"clickExclude": "Kliknutím vyberete výjimku",
|
||||||
|
"compress": "Komprimovat zálohu",
|
||||||
|
"confirm": "Potvrdit",
|
||||||
|
"confirmDelete": "Chcete tuto zálohu odstranit? Tuto akci nelze vrátit zpět.",
|
||||||
|
"confirmRestore": "Jste si jisti, že chcete provést obnovu z této zálohy. Všechny aktuální soubory serveru se změní na stav zálohy a nebude možné je obnovit.",
|
||||||
|
"currentBackups": "Aktuální zálohy",
|
||||||
|
"delete": "Smazat",
|
||||||
|
"destroyBackup": "Zničit zálohu \" + file_to_del + \"?",
|
||||||
|
"download": "Stáhnout",
|
||||||
|
"excludedBackups": "Vyloučené cesty: ",
|
||||||
|
"excludedChoose": "Vyberte cesty, které chcete ze zálohování vyloučit.",
|
||||||
|
"exclusionsTitle": "Vyloučení ze zálohování",
|
||||||
|
"maxBackups": "Maximální počet záloh",
|
||||||
|
"maxBackupsDesc": "Crafty neuloží více než N záloh a odstraní nejstarší (zadejte 0 pro zachování všech).",
|
||||||
|
"options": "Nastavení",
|
||||||
|
"path": "Cesta",
|
||||||
|
"restore": "Obnovit",
|
||||||
|
"restoring": "Obnovení zálohy. To může chvíli trvat. Buďte prosím trpěliví.",
|
||||||
|
"save": "Uložit",
|
||||||
|
"shutdown": "Vypnout server po dobu zálohování",
|
||||||
|
"size": "Velikost",
|
||||||
|
"storageLocation": "Umístění úložiště",
|
||||||
|
"storageLocationDesc": "Kam chcete ukládat zálohy?"
|
||||||
|
},
|
||||||
|
"serverConfig": {
|
||||||
|
"bePatientDelete": "Buďte prosím trpěliví, než odstraníme váš server z panelu Crafty. Tato obrazovka se za chvíli zavře.",
|
||||||
|
"bePatientDeleteFiles": "Buďte prosím trpěliví, než odstraníme váš server z panelu Crafty a všechny jeho soubory. Tato obrazovka se za chvíli zavře.",
|
||||||
|
"bePatientUpdate": "Prosím, buďte trpěliví, dokud server neaktualizujeme. Doba stahování se může lišit v závislosti na rychlosti vašeho internetu.<br /> Tato obrazovka se za chvíli aktualizuje",
|
||||||
|
"cancel": "Zrušit",
|
||||||
|
"crashTime": "Časový limit havárie",
|
||||||
|
"crashTimeDesc": "Jak dlouho bychom měli čekat, než budeme váš server považovat za havarovaný?",
|
||||||
|
"deleteFilesQuestion": "Odstranit soubory serveru z přístroje?",
|
||||||
|
"deleteFilesQuestionMessage": "Chcete, aby Crafty odstranil všechny soubory serveru z hostitelského počítače? <br><br><strong>To se týká i zálohování serverů.</strong>",
|
||||||
|
"deleteServer": "Odstranit server",
|
||||||
|
"deleteServerQuestion": "Odstranit server?",
|
||||||
|
"deleteServerQuestionMessage": "Opravdu chcete tento server odstranit? Po tomto kroku již není cesty zpět...",
|
||||||
|
"exeUpdateURL": "Adresa URL pro aktualizaci spustitelných souborů serveru",
|
||||||
|
"exeUpdateURLDesc": "Adresa URL pro přímé stahování aktualizací.",
|
||||||
|
"javaNoChange": "Nepřepisujte",
|
||||||
|
"javaVersion": "Přepsat aktuální verzi Javy",
|
||||||
|
"javaVersionDesc": "Pokud se chystáte přepsat Javu, ujistěte se, že je aktuální cesta k Javě v příkazu 'execution command' zabalena do uvozovek (výchozí proměnná 'java' je vyloučena).",
|
||||||
|
"noDelete": "Ne, vrať se zpět",
|
||||||
|
"noDeleteFiles": "Ne, stačí jen vyjmout z panelu",
|
||||||
|
"removeOldLogsAfter": "Odstranit staré protokoly po",
|
||||||
|
"removeOldLogsAfterDesc": "Kolik dní musí být soubor protokolu starý, aby byl smazán (0 je vypnuto)",
|
||||||
|
"save": "Uložit",
|
||||||
|
"sendingDelete": "Odstraňování serveru",
|
||||||
|
"sendingRequest": "Odeslání žádosti...",
|
||||||
|
"serverAutoStart": "Automatické spuštění serveru",
|
||||||
|
"serverAutostartDelay": "Zpoždění automatického spuštění serveru",
|
||||||
|
"serverAutostartDelayDesc": "Zpoždění před automatickým spuštěním (je-li povoleno níže)",
|
||||||
|
"serverCrashDetection": "Detekce pádu serveru",
|
||||||
|
"serverExecutable": "Spustitelný soubor serveru",
|
||||||
|
"serverExecutableDesc": "Spustitelný soubor pro server",
|
||||||
|
"serverExecutionCommand": "Příkaz pro spuštění serveru",
|
||||||
|
"serverExecutionCommandDesc": "Co se spustí ve skrytém terminálu",
|
||||||
|
"serverIP": "Adresa serveru",
|
||||||
|
"serverIPDesc": "IP adresa, ke které by se měl Crafty připojit pro statistiky (pokud máte problémy, zkuste místo 127.0.0.1 zadat skutečnou IP adresu).",
|
||||||
|
"serverLogLocation": "Umístění protokolu serveru",
|
||||||
|
"serverLogLocationDesc": "Cesta k souboru protokolu",
|
||||||
|
"serverName": "Název serveru",
|
||||||
|
"serverNameDesc": "Jak chcete aby se tento server jmenoval",
|
||||||
|
"serverPath": "Pracovní adresář serveru",
|
||||||
|
"serverPathDesc": "Absolutní úplná cesta (bez spustitelného souboru)",
|
||||||
|
"serverPort": "Port serveru",
|
||||||
|
"serverPortDesc": "Port Crafty should connect to for stats",
|
||||||
|
"serverStopCommand": "Příkaz pro zastavení serveru",
|
||||||
|
"serverStopCommandDesc": "Příkaz k odeslání programu pro jeho zastavení",
|
||||||
|
"showStatus": "Zobrazit na veřejné stavové stránce",
|
||||||
|
"stopBeforeDeleting": "Před odstraněním serveru jej prosím zastavte",
|
||||||
|
"update": "Aktualizovat spustitelný soubor",
|
||||||
|
"yesDelete": "Ano, smazat",
|
||||||
|
"yesDeleteFiles": "Ano, smazat soubory",
|
||||||
|
"shutdownTimeout": "Časový limit pro vypnutí",
|
||||||
|
"timeoutExplain1": "Jak dlouho bude Crafty čekat na vypnutí serveru po provedení příkazu",
|
||||||
|
"timeoutExplain2": "než proces ukončí."
|
||||||
|
},
|
||||||
|
"serverConfigHelp": {
|
||||||
|
"desc": "Zde můžete změnit konfiguraci serveru.",
|
||||||
|
"perms": [
|
||||||
|
"Doporučujeme <code>NEMĚNIT</code> cesty serveru spravované Craftym.",
|
||||||
|
"Změna cest <code>MŮŽE</code> něco rozbít, zejména v operačních systémech typu Linux, kde jsou práva k souborům více uzamčena.",
|
||||||
|
"<br /><br/>",
|
||||||
|
"Pokud máte pocit, že musíte změnit místo, kde je server umístěn, můžete tak učinit, pokud dáte uživateli \"crafty\" oprávnění ke čtení / zápisu k cestě k serveru.",
|
||||||
|
"<br />",
|
||||||
|
"<br />",
|
||||||
|
"V systému Linux to nejlépe provedete následujícím příkazem:<br />",
|
||||||
|
"<code>",
|
||||||
|
" sudo chown crafty:crafty /cesta/k/vašemu/serveru -R<br />",
|
||||||
|
" sudo chmod 2775 /cesta/k/vašemu/serveru -R<br />",
|
||||||
|
"</code>"
|
||||||
|
],
|
||||||
|
"title": "Oblast nastavení serveru"
|
||||||
|
},
|
||||||
|
"serverDetails": {
|
||||||
|
"backup": "Záloha",
|
||||||
|
"config": "Nastavení",
|
||||||
|
"files": "Soubory",
|
||||||
|
"logs": "Protokoly",
|
||||||
|
"playerControls": "Správa hráčů",
|
||||||
|
"schedule": "Harmonogram",
|
||||||
|
"serverDetails": "Podrobnosti o serveru",
|
||||||
|
"terminal": "Terminál",
|
||||||
|
"metrics": "Metrika",
|
||||||
|
"reset": "Obnovit posuvník",
|
||||||
|
"filter": "Filtrovat protokoly",
|
||||||
|
"filterList": "Filtrovaná slova"
|
||||||
|
},
|
||||||
|
"serverFiles": {
|
||||||
|
"clickUpload": "Klikněte sem a vyberte své soubory",
|
||||||
|
"close": "Zavřít",
|
||||||
|
"createDir": "Vytvořit složku",
|
||||||
|
"createDirQuestion": "Jaký chcete pojmenovat novou složku?",
|
||||||
|
"createFile": "Vytvořit soubor",
|
||||||
|
"createFileQuestion": "Jaké jméno chcete zvolit pro nový soubor?",
|
||||||
|
"default": "Výchozí",
|
||||||
|
"delete": "Smazat",
|
||||||
|
"deleteItemQuestion": "Jste si jisti, že chcete odstranit \" + name + \"?",
|
||||||
|
"deleteItemQuestionMessage": "Odstraňujete \\\"\" + path + \"\\\"!<br/><br/>Tato akce bude nevratná a navždy ztracená!",
|
||||||
|
"download": "Stáhnout",
|
||||||
|
"editingFile": "Upravit soubor",
|
||||||
|
"error": "Chyba při získání souborů",
|
||||||
|
"fileReadError": "Chyba při čtení souboru",
|
||||||
|
"files": "Soubory",
|
||||||
|
"keybindings": "Klávesové zkratky",
|
||||||
|
"loadingRecords": "Načítání souborů...",
|
||||||
|
"noDelete": "Ne",
|
||||||
|
"noscript": "Správce souborů nefunguje bez JavaScriptu",
|
||||||
|
"rename": "Přejmenovat",
|
||||||
|
"renameItemQuestion": "Jaký by měl být nový název?",
|
||||||
|
"save": "Uložit",
|
||||||
|
"size": "Přepnout velikost editoru",
|
||||||
|
"stayHere": "NEOPOUŠTĚJTE TUTO STRÁNKU!",
|
||||||
|
"unsupportedLanguage": "Varování: Tento typ souboru není podporovaný",
|
||||||
|
"unzip": "Rozbalit",
|
||||||
|
"upload": "Nahrát",
|
||||||
|
"uploadTitle": "Nahrát soubory do: ",
|
||||||
|
"waitUpload": "Počkejte prosím, než se vaše soubory nahrají... To může chvíli trvat.",
|
||||||
|
"yesDelete": "Ano, chápu následky"
|
||||||
|
},
|
||||||
|
"serverPlayerManagement": {
|
||||||
|
"bannedPlayers": "Zabanovaní hráči",
|
||||||
|
"loadingBannedPlayers": "Načítání zabanovaných hráčů",
|
||||||
|
"players": "Hráči"
|
||||||
|
},
|
||||||
|
"serverScheduleConfig": {
|
||||||
|
"backup": "Zálohovat server",
|
||||||
|
"select": "Základní / Cron / Řetězová reakce",
|
||||||
|
"basic": "Základní",
|
||||||
|
"children": "Propojené dětské úlohy:",
|
||||||
|
"command": "Příkaz",
|
||||||
|
"command-explain": "Jaký příkaz máme provést? Neuvádějte znak '/'",
|
||||||
|
"cron": "Cron",
|
||||||
|
"cron-explain": "Zadejte řetězec cronu -- POZNÁMKA: 0 = Pondělí v poslední možnosti.",
|
||||||
|
"custom": "Vlastní příkaz",
|
||||||
|
"days": "Dny",
|
||||||
|
"enabled": "Zapnuto",
|
||||||
|
"hours": "Hodiny",
|
||||||
|
"interval": "Interval",
|
||||||
|
"interval-explain": "Jak často chcete, aby se tento plán prováděl?",
|
||||||
|
"minutes": "Minuty",
|
||||||
|
"offset": "Posunutí prodlevy",
|
||||||
|
"offset-explain": "Jak dlouho bychom měli čekat na spuštění této úlohy po spuštění první úlohy? (sekund)",
|
||||||
|
"one-time": "Odstranit po provedení",
|
||||||
|
"parent": "Výběr plánu rodiče",
|
||||||
|
"parent-explain": "Který plán by měl spustit tento?",
|
||||||
|
"reaction": "Reakce",
|
||||||
|
"restart": "Restartovat server",
|
||||||
|
"start": "Spustit server",
|
||||||
|
"stop": "Vypnout server",
|
||||||
|
"time": "Čas",
|
||||||
|
"time-explain": "V kolik hodin chcete, aby byl váš plán spuštěn?"
|
||||||
|
},
|
||||||
|
"serverSchedules": {
|
||||||
|
"scheduledTasks": "Naplánované úlohy",
|
||||||
|
"create": "Vytvořit nový plán",
|
||||||
|
"name": "Název",
|
||||||
|
"action": "Akce",
|
||||||
|
"command": "Příkaz",
|
||||||
|
"interval": "Interval",
|
||||||
|
"nextRun": "Příští spuštění",
|
||||||
|
"enabled": "Zapnuto",
|
||||||
|
"edit": "Upravit",
|
||||||
|
"every": "Každý",
|
||||||
|
"yes": "Ano",
|
||||||
|
"no": "Ne",
|
||||||
|
"cron": "Řetězec Crong",
|
||||||
|
"details": "Podrobnosti o plánu",
|
||||||
|
"child": "Dítě plánu s ID",
|
||||||
|
"areYouSure": "Odstranění naplánované úlohy?",
|
||||||
|
"close": "Zavřít",
|
||||||
|
"delete": "Odstranit",
|
||||||
|
"cancel": "Zrušit",
|
||||||
|
"cannotSee": "Nevidíte všechno?",
|
||||||
|
"cannotSeeOnMobile": "Kliknutím na naplánovanou úlohu získáte podrobné informace.",
|
||||||
|
"confirm": "Potvrdit",
|
||||||
|
"confirmDelete": "Chcete tuto naplánovanou úlohu odstranit? Tuto akci nelze vrátit zpět."
|
||||||
|
},
|
||||||
|
"serverStats": {
|
||||||
|
"cpuUsage": "Využití CPU",
|
||||||
|
"description": "Popis",
|
||||||
|
"errorCalculatingUptime": "Chyba při výpočtu doby provozu",
|
||||||
|
"memUsage": "Využití paměti",
|
||||||
|
"offline": "Offline",
|
||||||
|
"online": "Online",
|
||||||
|
"players": "Hráči",
|
||||||
|
"serverStarted": "Server spuštěn",
|
||||||
|
"serverStatus": "Stav serveru",
|
||||||
|
"serverTime": "UTC čas",
|
||||||
|
"serverTimeZone": "Časové pásmo serveru",
|
||||||
|
"serverUptime": "Doba provozu serveru",
|
||||||
|
"starting": "Zpožděný start",
|
||||||
|
"unableToConnect": "Nelze se připojit",
|
||||||
|
"version": "Verze"
|
||||||
|
},
|
||||||
|
"serverTerm": {
|
||||||
|
"commandInput": "Zadejte příkaz",
|
||||||
|
"delay-explained": "Služba/agent byla nedávno spuštěna a zpožďuje spuštění instance minecraft serveru.",
|
||||||
|
"importing": "Importování...",
|
||||||
|
"restart": "Restartovat",
|
||||||
|
"sendCommand": "Odeslat příkaz",
|
||||||
|
"start": "Spustit",
|
||||||
|
"starting": "Zpožděný start",
|
||||||
|
"stop": "Zastavit",
|
||||||
|
"stopScroll": "Zastavit automatické posouvání",
|
||||||
|
"updating": "Aktualizace...",
|
||||||
|
"installing": "Instalace..."
|
||||||
|
},
|
||||||
|
"serverMetrics": {
|
||||||
|
"resetZoom": "Reset Zoom",
|
||||||
|
"zoomHint1": "Chcete-li graf přiblížit, podržte klávesu Shift a poté použijte kolečko myši.",
|
||||||
|
"zoomHint2": "Případně můžete podržet klávesu Shift a kliknout a přetáhnout oblast, kterou chcete přiblížit."
|
||||||
|
},
|
||||||
|
"serverWizard": {
|
||||||
|
"absoluteServerPath": "Absolutní cesta k serveru",
|
||||||
|
"absoluteZipPath": "Absolutní cesta k serveru",
|
||||||
|
"addRole": "Přidání serveru k existujícím rolím",
|
||||||
|
"autoCreate": "Pokud nebude vybrána žádná, Crafty ji vyrobí!",
|
||||||
|
"bePatient": "Buďte prosím trpěliví, protože musíme ' + (importing ? 'importovat' : 'stáhnout') + ' server",
|
||||||
|
"buildServer": "Sestavit server!",
|
||||||
|
"clickRoot": "Klikněte zde pro výběr kořenového adresáře",
|
||||||
|
"close": "Zavřít",
|
||||||
|
"defaultPort": "25565 výchozí hodnota",
|
||||||
|
"downloading": "Stahování serveru...",
|
||||||
|
"explainRoot": "Klikněte na tlačítko níže a vyberte kořenový adresář vašeho serveru z archivu.",
|
||||||
|
"importing": "Importování serveru...",
|
||||||
|
"importServer": "Importování existujícího serveru",
|
||||||
|
"importServerButton": "Importovat server!",
|
||||||
|
"importZip": "Imporovat ze souboru Zip",
|
||||||
|
"uploadZip": "Nahrání souboru Zip pro importování serveru",
|
||||||
|
"maxMem": "Maximální paměť",
|
||||||
|
"minMem": "Minimální paměť",
|
||||||
|
"myNewServer": "Nový server",
|
||||||
|
"newServer": "Vytvořit nový server",
|
||||||
|
"quickSettings": "Rychlé nastavení",
|
||||||
|
"quickSettingsDescription": "Nebojte se, můžete je změnit později.",
|
||||||
|
"resetForm": "Obnovit nastavení formuláře",
|
||||||
|
"save": "Uložit",
|
||||||
|
"selectRole": "Vyberte roli(e)",
|
||||||
|
"selectRoot": "Vyberte kořenový adresář archivu",
|
||||||
|
"selectType": "Typ serveru (Vanilla, Servery, Modované atd.)",
|
||||||
|
"selectServer": "Vyberte server",
|
||||||
|
"selectVersion": "Vyberte verzi",
|
||||||
|
"selectZipDir": "Vyberte adresář v archivu, ze kterého mají být soubory rozbaleny.",
|
||||||
|
"serverJar": "Soubor spustitelný serverem",
|
||||||
|
"serverName": "Název serveru",
|
||||||
|
"serverPath": "Cesta k serveru",
|
||||||
|
"serverPort": "Port serveru",
|
||||||
|
"serverType": "Typ serveru",
|
||||||
|
"serverSelect": "Výběr serveru",
|
||||||
|
"serverVersion": "Verze serveru",
|
||||||
|
"sizeInGB": "Velikost v GB",
|
||||||
|
"zipPath": "Cesta k serveru"
|
||||||
|
},
|
||||||
|
"sidebar": {
|
||||||
|
"contribute": "Přispět",
|
||||||
|
"credits": "Zásluhy",
|
||||||
|
"dashboard": "Ovládací panel",
|
||||||
|
"documentation": "Dokumentace",
|
||||||
|
"navigation": "Navigace",
|
||||||
|
"newServer": "Vytvořit nový server",
|
||||||
|
"servers": "Servery"
|
||||||
|
},
|
||||||
|
"userConfig": {
|
||||||
|
"apiKey": "Klíče API",
|
||||||
|
"auth": "Autorizovaný? ",
|
||||||
|
"config": "Nastavení",
|
||||||
|
"configArea": "Uživatelská nastavení",
|
||||||
|
"configAreaDesc": "Zde můžete změnit všechna nastavení uživatele.",
|
||||||
|
"confirmDelete": "Jste si jisti, že chcete tohoto uživatele odstranit? Tato akce je nevratná.",
|
||||||
|
"craftyPermDesc": "Oprávnění tohoto uživatele ",
|
||||||
|
"craftyPerms": "Oprávnění: ",
|
||||||
|
"created": "Vytvořeno: ",
|
||||||
|
"deleteUser": "Smazat uživatele: ",
|
||||||
|
"deleteUserB": "Smazat uživatele",
|
||||||
|
"delSuper": "Super uživatele nelze odstranit",
|
||||||
|
"enabled": "Povolen",
|
||||||
|
"gravDesc": "Tento e-mail je určen výhradně pro použití se službou Gravatar™. Crafty nebude v žádném případě používat tento e-mail k ničemu jinému než k vyhledávání vašeho Gravataru™.",
|
||||||
|
"gravEmail": "Gravatar™ E-mail",
|
||||||
|
"lastIP": "Poslední IP: ",
|
||||||
|
"lastLogin": "Poslední přihlášení: ",
|
||||||
|
"lastUpdate": "Poslední aktualizace: ",
|
||||||
|
"leaveBlank": "Chcete-li upravit uživatele beze změny hesla, ponechte pole prázdné.",
|
||||||
|
"member": "Člen?",
|
||||||
|
"notExist": "Nemůžete odstranit něco, co neexistuje!",
|
||||||
|
"pageTitle": "Upravit uživatele",
|
||||||
|
"pageTitleNew": "Vytvořit uživatele",
|
||||||
|
"password": "Nové heslo",
|
||||||
|
"permName": "Název povolení",
|
||||||
|
"repeat": "Zopakujte heslo",
|
||||||
|
"roleName": "Název role",
|
||||||
|
"super": "Super uživatel",
|
||||||
|
"userLang": "Jazyk uživatele",
|
||||||
|
"userTheme": "Motiv UI",
|
||||||
|
"userName": "Uživatelské jméno",
|
||||||
|
"userNameDesc": "Jak chcete aby se tento uživatel jmenoval?",
|
||||||
|
"userRoles": "Role uživatele",
|
||||||
|
"userRolesDesc": "Role, jejichž je tento uživatel členem.",
|
||||||
|
"userSettings": "Nastavení uživatele",
|
||||||
|
"uses": "Počet povolených použití (-1==bez omezení)",
|
||||||
|
"manager": "Správce",
|
||||||
|
"selectManager": "Vyberte Správce pro uživatele"
|
||||||
|
}
|
||||||
|
}
|
@ -213,6 +213,8 @@
|
|||||||
"assignedRoles": "Assigned Roles",
|
"assignedRoles": "Assigned Roles",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"clearComms": "Clear Un-executed Commands",
|
"clearComms": "Clear Un-executed Commands",
|
||||||
|
"select": "Select",
|
||||||
|
"apply": "Apply",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"enabled": "Enabled",
|
"enabled": "Enabled",
|
||||||
@ -228,12 +230,20 @@
|
|||||||
"superConfirmTitle": "Enable superuser? Are you sure?",
|
"superConfirmTitle": "Enable superuser? Are you sure?",
|
||||||
"user": "User",
|
"user": "User",
|
||||||
"users": "Users",
|
"users": "Users",
|
||||||
|
"title": "Crafty Configuration"
|
||||||
|
},
|
||||||
|
"customLogin": {
|
||||||
|
"customLoginPage": "Customise the Login Page",
|
||||||
"loginImage": "Upload a background image for the login screen.",
|
"loginImage": "Upload a background image for the login screen.",
|
||||||
"backgroundUpload": "Background Upload",
|
"backgroundUpload": "Background Upload",
|
||||||
"loginBackground": "Login Background Image",
|
"loginBackground": "Login Background Image",
|
||||||
|
"loginOpacity": "Select the Login Window Opacity",
|
||||||
"select": "Select",
|
"select": "Select",
|
||||||
|
"apply": "Apply",
|
||||||
|
"delete": "Delete",
|
||||||
"selectImage": "Select an image",
|
"selectImage": "Select an image",
|
||||||
"preview": "Preview"
|
"preview": "Preview",
|
||||||
|
"pageTitle": "Custom Login Page"
|
||||||
},
|
},
|
||||||
"rolesConfig": {
|
"rolesConfig": {
|
||||||
"config": "Role Config",
|
"config": "Role Config",
|
||||||
|
@ -1,20 +1,27 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<Container version="2">
|
<Container version="2">
|
||||||
<Beta>True</Beta>
|
<Beta>False</Beta>
|
||||||
<Name>Crafty-4</Name>
|
<Name>Crafty-4</Name>
|
||||||
<Repository>registry.gitlab.com/crafty-controller/crafty-4:latest</Repository>
|
<Repository>arcadiatechnology/crafty-4</Repository>
|
||||||
<Registry>registry.gitlab.com/crafty-controller/crafty-4</Registry>
|
<Registry>https://hub.docker.com/r/arcadiatechnology/crafty-4</Registry>
|
||||||
<Network>bridge</Network>
|
<Network>bridge</Network>
|
||||||
<MyIP/>
|
<MyIP/>
|
||||||
<Shell>sh</Shell>
|
<Shell>sh</Shell>
|
||||||
<Privileged>false</Privileged>
|
<Privileged>false</Privileged>
|
||||||
<Support>https://discord.gg/9VJPhCE</Support>
|
<Support>https://discord.gg/9VJPhCE</Support>
|
||||||
<Project>https://craftycontrol.com/</Project>
|
<Project>https://craftycontrol.com/</Project>
|
||||||
<Overview>Crafty 4 is the next iteration of our Minecraft Server Wrapper / Controller / Launcher. Boasting a clean new look, rebuilt from the ground up. Crafty 4 brings a whole host of new features such as Bedrock support. With SteamCMD support on the way!
|
<License>GNU GPL V3</License>
|
||||||
Default login Credentrails are username: "admin" password: "crafty"
|
<Branch>
|
||||||
Crafty 4 is the successor of Crafty Controller, the Docker image is no longer maintained on DockerHub. (now on GitLab)
|
<Tag>latest</Tag>
|
||||||
For official support join the Discord: https://discord.gg/9VJPhCE
|
<TagDescription>Latest version of Crafty, which should be used for production purposes as it is the most stable</TagDescription>
|
||||||
For migration from 3.x please refer to the documentation: https://wiki.craftycontrol.com/en/4/
|
</Branch>
|
||||||
|
<Branch>
|
||||||
|
<Tag>dev</Tag>
|
||||||
|
<TagDescription>Development version of Crafty, only generally used for testing purposes, because of its less stable nature</TagDescription>
|
||||||
|
</Branch>
|
||||||
|
<Screenshot>https://wiki.craftycontrol.com/uploads/en/crafty%204%20dashboard%20with%20one%20server.jpeg</Screenshot>
|
||||||
|
<Screenshot>https://wiki.craftycontrol.com/uploads/en/crafty%204%20server%20setup%20details.png</Screenshot>
|
||||||
|
<Overview>Crafty 4 is the next iteration of our Minecraft Server Wrapper / Controller / Launcher. [br]Boasting a clean new look, rebuilt from the ground up. [br] [br] Crafty 4 brings a whole host of new features such as Bedrock support. [br] With SteamCMD support on the way![br] **Default login Credentrails are username: "admin" password: "crafty". ** [br]Crafty 4 is the successor of Crafty Controller. [br]For official support join the Discord server https://discord.gg/9VJPhCE [br] For migration from 3.x please refer to the documentation: https://wiki.craftycontrol.com/en/4/
|
||||||
</Overview>
|
</Overview>
|
||||||
<Category>GameServers: Other:</Category>
|
<Category>GameServers: Other:</Category>
|
||||||
<WebUI>https://[IP]:[PORT:8443]/</WebUI>
|
<WebUI>https://[IP]:[PORT:8443]/</WebUI>
|
||||||
@ -26,12 +33,6 @@ For migration from 3.x please refer to the documentation: https://wiki.craftycon
|
|||||||
<DateInstalled/>
|
<DateInstalled/>
|
||||||
<DonateText>Help to support Crafty on Kofi</DonateText>
|
<DonateText>Help to support Crafty on Kofi</DonateText>
|
||||||
<DonateLink>https://ko-fi.com/arcadiatech</DonateLink>
|
<DonateLink>https://ko-fi.com/arcadiatech</DonateLink>
|
||||||
<Description>Crafty 4 is the next iteration of our Minecraft Server Wrapper / Controller / Launcher. Boasting a clean new look, rebuilt from the ground up. Crafty 4 brings a whole host of new features such as Bedrock support. With SteamCMD support on the way!
|
|
||||||
Default login Credentrails are username: "admin" password: "crafty"
|
|
||||||
Crafty 4 is the successor of Crafty Controller, the Docker image is no longer maintained on DockerHub. (now on GitLab)
|
|
||||||
For official support join the Discord: https://discord.gg/9VJPhCE
|
|
||||||
For migration from 3.x please refer to the documentation: https://wiki.craftycontrol.com/en/4/
|
|
||||||
</Description>
|
|
||||||
<Networking>
|
<Networking>
|
||||||
<Mode>bridge</Mode>
|
<Mode>bridge</Mode>
|
||||||
<Publish>
|
<Publish>
|
||||||
|
1
main.py
1
main.py
@ -219,7 +219,6 @@ if __name__ == "__main__":
|
|||||||
Console.debug(f"Execution Mode: {running_mode}")
|
Console.debug(f"Execution Mode: {running_mode}")
|
||||||
Console.debug(f"Application path : '{application_path}'")
|
Console.debug(f"Application path : '{application_path}'")
|
||||||
|
|
||||||
controller.clear_unexecuted_commands()
|
|
||||||
controller.clear_support_status()
|
controller.clear_support_status()
|
||||||
|
|
||||||
crafty_prompt = MainPrompt(
|
crafty_prompt = MainPrompt(
|
||||||
|
Loading…
Reference in New Issue
Block a user