mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'dev' into tweak/file-enhancements
This commit is contained in:
commit
610b94dd59
@ -21,7 +21,7 @@ win-dev-build:
|
|||||||
- pyinstaller -F main.py
|
- pyinstaller -F main.py
|
||||||
--distpath .
|
--distpath .
|
||||||
--icon app\frontend\static\assets\images\Crafty_4-0_Logo_square.ico
|
--icon app\frontend\static\assets\images\Crafty_4-0_Logo_square.ico
|
||||||
--name "crafty_commander"
|
--name "crafty"
|
||||||
--paths .venv\Lib\site-packages
|
--paths .venv\Lib\site-packages
|
||||||
--hidden-import cryptography
|
--hidden-import cryptography
|
||||||
--hidden-import cffi
|
--hidden-import cffi
|
||||||
@ -37,7 +37,7 @@ win-dev-build:
|
|||||||
name: "crafty-${CI_RUNNER_TAGS}-${CI_COMMIT_BRANCH}_${CI_COMMIT_SHORT_SHA}"
|
name: "crafty-${CI_RUNNER_TAGS}-${CI_COMMIT_BRANCH}_${CI_COMMIT_SHORT_SHA}"
|
||||||
paths:
|
paths:
|
||||||
- app\
|
- app\
|
||||||
- .\crafty_commander.exe
|
- .\crafty.exe
|
||||||
exclude:
|
exclude:
|
||||||
- app\classes\**\*
|
- app\classes\**\*
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ win-prod-build:
|
|||||||
- pyinstaller -F main.py
|
- pyinstaller -F main.py
|
||||||
--distpath .
|
--distpath .
|
||||||
--icon app\frontend\static\assets\images\Crafty_4-0_Logo_square.ico
|
--icon app\frontend\static\assets\images\Crafty_4-0_Logo_square.ico
|
||||||
--name "crafty_commander"
|
--name "crafty"
|
||||||
--paths .venv\Lib\site-packages
|
--paths .venv\Lib\site-packages
|
||||||
--hidden-import cryptography
|
--hidden-import cryptography
|
||||||
--hidden-import cffi
|
--hidden-import cffi
|
||||||
@ -81,7 +81,7 @@ win-prod-build:
|
|||||||
name: "crafty-${CI_RUNNER_TAGS}-${CI_COMMIT_BRANCH}_${CI_COMMIT_SHORT_SHA}"
|
name: "crafty-${CI_RUNNER_TAGS}-${CI_COMMIT_BRANCH}_${CI_COMMIT_SHORT_SHA}"
|
||||||
paths:
|
paths:
|
||||||
- app\
|
- app\
|
||||||
- .\crafty_commander.exe
|
- .\crafty.exe
|
||||||
expire_in: never
|
expire_in: never
|
||||||
exclude:
|
exclude:
|
||||||
- app\classes\**\*
|
- app\classes\**\*
|
||||||
|
@ -232,6 +232,8 @@ function-naming-style=snake_case
|
|||||||
good-names=e,
|
good-names=e,
|
||||||
ex,
|
ex,
|
||||||
f,
|
f,
|
||||||
|
fd,
|
||||||
|
fn,
|
||||||
i,
|
i,
|
||||||
id,
|
id,
|
||||||
ip,
|
ip,
|
||||||
|
48
CHANGELOG.md
48
CHANGELOG.md
@ -1,43 +1,51 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [4.0.3] - TBD
|
## --- [4.0.4] - 2022/06/21
|
||||||
|
|
||||||
### New features
|
### New features
|
||||||
TBD
|
- Add shutdown on backup feature ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/373))
|
||||||
|
- Add detection and dropdown of java versions ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/375))
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
TBD
|
- Backup/Config.json rework for API key hardening ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/369))
|
||||||
|
- Fix stack on ping result being falsy ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/371))
|
||||||
|
- Fix sec bug with server creation roles ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/376))
|
||||||
### Tweaks
|
### Tweaks
|
||||||
- Use 4 space indentation for the session file.
|
- Spelling mistake fixed in German lang file ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/370))
|
||||||
- Use with-blocks when opening files to prevent them from being left open
|
- Backup failure warning (Tab text goes red) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/373))
|
||||||
|
- Rework server list on dashboard display for use on small screens ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/372))
|
||||||
|
- File handling enhancements ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/362))
|
||||||
|
<br><br>
|
||||||
|
|
||||||
## [4.0.2] - 2022/06/16
|
## --- [4.0.3] - 2022/06/18
|
||||||
|
### New features
|
||||||
|
- Integrate Wiki iframe into panel instead of link ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/367))
|
||||||
|
### Bug fixes
|
||||||
|
- Amend Java system variable fix to be more specfic since they only affect Oracle. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/364))
|
||||||
|
- API Token authentication hardening ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/364))
|
||||||
|
### Tweaks
|
||||||
|
- Add better error logging for statistic collection ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/359))
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
## --- [4.0.2-hotfix1] - 2022/06/17
|
||||||
|
### Crit Bug fixes
|
||||||
|
- Fix blank server_detail page for general users ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/358))
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
## --- [4.0.2] - 2022/06/16
|
||||||
### New features
|
### New features
|
||||||
None
|
None
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
- Fix winreg import pass on non-NT systems ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/344))
|
- Fix winreg import pass on non-NT systems ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/344))
|
||||||
- Make the WebSocket automatically reconnect. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/345))
|
- Make the WebSocket automatically reconnect. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/345))
|
||||||
- Fix an error when there are no servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/346))
|
|
||||||
- Use relative paths for the jarfile and logs ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/347))
|
|
||||||
- Flatten all instances of username creation or editing, usernames should be lower case.
|
|
||||||
- - ([Merge Request 1](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/342))
|
|
||||||
- - ([Merge Request 2](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/351))
|
- - ([Merge Request 2](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/351))
|
||||||
- Add version inheretence & config check ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/353))
|
- Add version inheretence & config check ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/353))
|
||||||
- Fix support log temp file deletion issue/hang ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/354))
|
- Fix support log temp file deletion issue/hang ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/354))
|
||||||
|
<br><br>
|
||||||
|
|
||||||
## [4.0.1] - 2022/06/15
|
## --- [4.0.1] - 2022/06/15
|
||||||
|
|
||||||
### New features
|
### New features
|
||||||
None
|
None
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|
||||||
- Remove session.lock warning ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/338))
|
- Remove session.lock warning ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/338))
|
||||||
- Correct Dutch Spacing Issue ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/340))
|
- Correct Dutch Spacing Issue ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/340))
|
||||||
- Remove no-else-* pylint exemptions and tidy code. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/342))
|
- Remove no-else-* pylint exemptions and tidy code. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/342))
|
||||||
- Make unRAID more readable, and flatten path to lower, to fit standard practice. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/337))
|
|
||||||
- Fix Java Pathing issues on windows ([Commit](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/343/diffs?commit_id=cda2120579083d447db5dbeb5489822880f4cae7))
|
|
||||||
|
|
||||||
|
68
CONTRIBUTING.md
Normal file
68
CONTRIBUTING.md
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# Crafty 4 - A contributors guide.
|
||||||
|
*Don't Panic!*<br><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!
|
||||||
|
|
||||||
|
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>
|
||||||
|
## Getting started
|
||||||
|
There are two incredibly helpful ways of contributing to the project: `Issues` and `Changes`.
|
||||||
|
|
||||||
|
### Issues
|
||||||
|
|
||||||
|
#### Create a new issue
|
||||||
|
|
||||||
|
If you spot a problem with crafty, [search if an issue already exists](https://gitlab.com/crafty-controller/crafty-4/-/issues). If a related issue doesn't exist, you can [open a new issue](https://gitlab.com/crafty-controller/crafty-4/-/issues/new) using a relevant issue template:
|
||||||
|
- Bug - For any bugs you may find in Crafty.
|
||||||
|
- Feature Request - For any features you'd like to see in Crafty.
|
||||||
|
- Change Request - For any changes you'd like to see to existing Crafty functions.
|
||||||
|
|
||||||
|
#### Solve an issue
|
||||||
|
|
||||||
|
If you're feeling inclined and want to help us with our workload you can have a look through our [existing issues](https://gitlab.com/crafty-controller/crafty-4/-/issues) to find one that interests you. (You can narrow down the search using `labels` as filters.)
|
||||||
|
|
||||||
|
### Make changes
|
||||||
|
|
||||||
|
1. [Install Git](https://docs.gitlab.com/ee/topics/git/how_to_install_git/).
|
||||||
|
|
||||||
|
2. Fork the repository.
|
||||||
|
|
||||||
|
3. Create a branch from `dev` with a suitable name that matches our folder branch flow<br> `bugfix/` `tweak/` `lang/` `feature/`<br>
|
||||||
|
For Example:<br>
|
||||||
|
`tweak/websocket-auto-reconnect`<br>
|
||||||
|
`bugfix/blank-page-as-non-superuser`<br>
|
||||||
|
`lang/german-spelling-correction`<br>
|
||||||
|
`feature/support-log-downloader`
|
||||||
|
|
||||||
|
4. Make your changes!
|
||||||
|
|
||||||
|
5. Make sure your code is formatted correctly ([We use Black](https://black.readthedocs.io/en/stable/getting_started.html)).
|
||||||
|
> 🧑🎓 If you are using **VSCODE** you can follow this [handy dandy tiny guide](https://marcobelo.medium.com/setting-up-python-black-on-visual-studio-code-5318eba4cd00) on how to setup formatting on save.<br>
|
||||||
|
This will allow you to write your code without having to think about ⬛**black**, and then when you press `ctrl+s` black will immediately format your code!
|
||||||
|
|
||||||
|
|
||||||
|
### Commit your update
|
||||||
|
|
||||||
|
Commit your changes once you are happy with them. See [Chris Beam's guide](https://chris.beams.io/posts/git-commit/) on how to write a suitable commit message. This will be enforced. If your commit messages don't meet suitable standards, your merge request will not be merged.
|
||||||
|
|
||||||
|
- Please make sure and test the area that you have been working in, this makes our reviewers' lives easier!
|
||||||
|
|
||||||
|
### Create a merge request
|
||||||
|
|
||||||
|
Once you are all done making your changes make a MR (merge request) into our `dev` branch.
|
||||||
|
|
||||||
|
- Fill in the merge request template. This template helps reviewers understand your changes as well as the purpose of your merge request. Make sure to include details!
|
||||||
|
- If you are solving an issue don't forget to link the MR to that issue.
|
||||||
|
- Make sure to [allow upstream commits](https://docs.gitlab.com/ee/user/project/merge_requests/allow_collaboration.html#allow-commits-from-upstream-members) so we can prepare the branch for merge if it's not quite right. <br> A member of the maintainer team will review your proposal. We may also ask questions or request additional information at this stage. On some occasions we may reject your proposal, please don't be disheartened if we do so. Even if your code does not make it into the repo we appreciate your time and effort spent on creating the MR.
|
||||||
|
- Please make sure your merge request complies with the pylint's Code Climate report on your MR and fix any issues that are raised.
|
||||||
|
- We may ask for changes to be made before a MR can be merged, either using [suggested changes](https://docs.gitlab.com/ee/user/project/merge_requests/reviews/suggestions.html), inline comments or merge request threads. You can apply suggested changes directly through GitLab's UI. You can also make any other changes in your fork, then commit them to your branch before the merge request is processed.
|
||||||
|
- As you update your MR with changes we request, mark each thread as [resolved](https://docs.gitlab.com/ee/user/discussions/#resolve-a-thread).
|
||||||
|
- If you run into any merge issues checkout this [git tutorial](https://about.gitlab.com/blog/2016/09/06/resolving-merge-conflicts-from-the-gitlab-ui/) to help you resolve them. (If you get stuck your reviewer can help you.)
|
||||||
|
|
||||||
|
### Your MR is merged!
|
||||||
|
|
||||||
|
Congratulations 🎉 You've successfully made a contribution to Crafty!
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
|
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
|
||||||
[![Supported Python Versions](https://shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20-blue)](https://www.python.org)
|
[![Supported Python Versions](https://shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20-blue)](https://www.python.org)
|
||||||
[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.2--beta-orange)](https://gitlab.com/crafty-controller/crafty-4/-/releases)
|
[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.4--beta-orange)](https://gitlab.com/crafty-controller/crafty-4/-/releases)
|
||||||
[![Code Quality(temp-hardcoded)](https://img.shields.io/badge/code%20quality-10-brightgreen)](https://gitlab.com/crafty-controller/crafty-4)
|
[![Code Quality(temp-hardcoded)](https://img.shields.io/badge/code%20quality-10-brightgreen)](https://gitlab.com/crafty-controller/crafty-4)
|
||||||
[![Build Status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master)
|
[![Build Status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master)
|
||||||
|
|
||||||
# Crafty Controller 4.0.2-beta
|
# Crafty Controller 4.0.4-beta
|
||||||
> Python based Control Panel for your Minecraft Server
|
> Python based Control Panel for your Minecraft Server
|
||||||
|
|
||||||
## What is Crafty Controller?
|
## What is Crafty Controller?
|
||||||
@ -39,7 +39,7 @@ With `Crafty Controller 4.0` we have focused on building our DevOps Principles,
|
|||||||
> __**⚠ 🔻WARNING: [WSL/WSL2 | WINDOWS 11 | DOCKER DESKTOP]🔻**__ <br>
|
> __**⚠ 🔻WARNING: [WSL/WSL2 | WINDOWS 11 | DOCKER DESKTOP]🔻**__ <br>
|
||||||
BE ADVISED! Upstream is currently broken for Minecraft running on **Docker under WSL/WSL2, Windows 11 / DOCKER DESKTOP!** <br>
|
BE ADVISED! Upstream is currently broken for Minecraft running on **Docker under WSL/WSL2, Windows 11 / DOCKER DESKTOP!** <br>
|
||||||
On '**Stop**' or '**Restart**' of the MC Server, there is a 90% chance the World's Chunks will be shredded irreparably! <br>
|
On '**Stop**' or '**Restart**' of the MC Server, there is a 90% chance the World's Chunks will be shredded irreparably! <br>
|
||||||
Please only run Docker on Linux, If you are using Windows we have a portable installs found here: [Latest-Stable](https://gitlab.com/crafty-controller/crafty-4/-/jobs/artifacts/master/download?job=win-prod-build), [Latest-Development](https://gitlab.com/crafty-controller/crafty-4/-/jobs/artifacts/dev/download?job=win-dev-build)
|
Please only run Docker on Linux, If you are using Windows we have a portable installs found here: [Latest-Stable](https://gitlab.com/crafty-controller/crafty-4/-/releases), [Latest-Development](https://gitlab.com/crafty-controller/crafty-4/-/jobs/artifacts/dev/download?job=win-dev-build)
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -17,6 +17,14 @@ class ManagementController:
|
|||||||
def get_latest_hosts_stats():
|
def get_latest_hosts_stats():
|
||||||
return HelpersManagement.get_latest_hosts_stats()
|
return HelpersManagement.get_latest_hosts_stats()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_crafty_api_key(key):
|
||||||
|
HelpersManagement.set_secret_api_key(key)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_crafty_api_key():
|
||||||
|
return HelpersManagement.get_secret_api_key()
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Commands Methods
|
# Commands Methods
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
@ -128,9 +136,10 @@ class ManagementController:
|
|||||||
max_backups: int = None,
|
max_backups: int = None,
|
||||||
excluded_dirs: list = None,
|
excluded_dirs: list = None,
|
||||||
compress: bool = False,
|
compress: bool = False,
|
||||||
|
shutdown: bool = False,
|
||||||
):
|
):
|
||||||
return self.management_helper.set_backup_config(
|
return self.management_helper.set_backup_config(
|
||||||
server_id, backup_path, max_backups, excluded_dirs, compress
|
server_id, backup_path, max_backups, excluded_dirs, compress, shutdown
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -5,6 +5,7 @@ import json
|
|||||||
import typing as t
|
import typing as t
|
||||||
|
|
||||||
from app.classes.controllers.roles_controller import RolesController
|
from app.classes.controllers.roles_controller import RolesController
|
||||||
|
from app.classes.shared.file_helpers import FileHelpers
|
||||||
|
|
||||||
from app.classes.shared.singleton import Singleton
|
from app.classes.shared.singleton import Singleton
|
||||||
from app.classes.shared.server import ServerInstance
|
from app.classes.shared.server import ServerInstance
|
||||||
@ -28,8 +29,9 @@ logger = logging.getLogger(__name__)
|
|||||||
class ServersController(metaclass=Singleton):
|
class ServersController(metaclass=Singleton):
|
||||||
servers_list: ServerInstance
|
servers_list: ServerInstance
|
||||||
|
|
||||||
def __init__(self, helper, servers_helper, management_helper):
|
def __init__(self, helper, servers_helper, management_helper, file_helper):
|
||||||
self.helper: Helpers = helper
|
self.helper: Helpers = helper
|
||||||
|
self.file_helper: FileHelpers = file_helper
|
||||||
self.servers_helper: HelperServers = servers_helper
|
self.servers_helper: HelperServers = servers_helper
|
||||||
self.management_helper = management_helper
|
self.management_helper = management_helper
|
||||||
self.servers_list = []
|
self.servers_list = []
|
||||||
@ -189,6 +191,7 @@ class ServersController(metaclass=Singleton):
|
|||||||
self.helper,
|
self.helper,
|
||||||
self.management_helper,
|
self.management_helper,
|
||||||
self.stats,
|
self.stats,
|
||||||
|
self.file_helper,
|
||||||
),
|
),
|
||||||
"server_settings": settings.props,
|
"server_settings": settings.props,
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,9 @@ class Stats:
|
|||||||
psutil.boot_time(), datetime.timezone.utc
|
psutil.boot_time(), datetime.timezone.utc
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"error while getting boot time due to {e}")
|
logger.debug(
|
||||||
|
"getting boot time failed due to the following error:", exc_info=e
|
||||||
|
)
|
||||||
# unix epoch with no timezone data
|
# unix epoch with no timezone data
|
||||||
return datetime.datetime.fromtimestamp(0, datetime.timezone.utc)
|
return datetime.datetime.fromtimestamp(0, datetime.timezone.utc)
|
||||||
|
|
||||||
@ -72,7 +74,9 @@ class Stats:
|
|||||||
try:
|
try:
|
||||||
return psutil.cpu_percent(interval=0.5) / psutil.cpu_count()
|
return psutil.cpu_percent(interval=0.5) / psutil.cpu_count()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"error while getting cpu percentage due to {e}")
|
logger.debug(
|
||||||
|
"getting the cpu usage failed due to the following error:", exc_info=e
|
||||||
|
)
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
def __init__(self, helper, controller):
|
def __init__(self, helper, controller):
|
||||||
@ -100,7 +104,9 @@ class Stats:
|
|||||||
"disk_data": Stats._try_all_disk_usage(),
|
"disk_data": Stats._try_all_disk_usage(),
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"error while getting host stats due to {e}")
|
logger.debug(
|
||||||
|
"getting host stats failed due to the following error:", exc_info=e
|
||||||
|
)
|
||||||
node_stats: NodeStatsDict = {
|
node_stats: NodeStatsDict = {
|
||||||
"boot_time": str(
|
"boot_time": str(
|
||||||
datetime.datetime.fromtimestamp(0, datetime.timezone.utc)
|
datetime.datetime.fromtimestamp(0, datetime.timezone.utc)
|
||||||
@ -124,50 +130,50 @@ class Stats:
|
|||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _try_get_process_stats(process):
|
def _try_get_process_stats(process, running):
|
||||||
try:
|
if running:
|
||||||
return Stats._get_process_stats(process)
|
try:
|
||||||
except Exception as e:
|
return Stats._get_process_stats(process)
|
||||||
logger.debug(f"error while getting process stats due to {e}")
|
except Exception as e:
|
||||||
return {"cpu_usage": -1, "memory_usage": -1, "mem_percentage": -1}
|
logger.debug(
|
||||||
|
f"getting process stats for pid {process.pid} "
|
||||||
|
"failed due to the following error:",
|
||||||
|
exc_info=e,
|
||||||
|
)
|
||||||
|
return {"cpu_usage": -1, "memory_usage": -1, "mem_percentage": -1}
|
||||||
|
else:
|
||||||
|
return {"cpu_usage": 0, "memory_usage": 0, "mem_percentage": 0}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_process_stats(process):
|
def _get_process_stats(process):
|
||||||
if process is None:
|
if process is None:
|
||||||
return {"cpu_usage": 0, "memory_usage": 0, "mem_percentage": 0}
|
return {"cpu_usage": -1, "memory_usage": -1, "mem_percentage": -1}
|
||||||
process_pid = process.pid
|
process_pid = process.pid
|
||||||
try:
|
p = psutil.Process(process_pid)
|
||||||
p = psutil.Process(process_pid)
|
_dummy = p.cpu_percent()
|
||||||
dummy = p.cpu_percent()
|
|
||||||
|
|
||||||
# call it first so we can be more accurate per the docs
|
# call it first so we can be more accurate per the docs
|
||||||
# https://giamptest.readthedocs.io/en/latest/#psutil.Process.cpu_percent
|
# https://giamptest.readthedocs.io/en/latest/#psutil.Process.cpu_percent
|
||||||
|
|
||||||
real_cpu = round(p.cpu_percent(interval=0.5) / psutil.cpu_count(), 2)
|
real_cpu = round(p.cpu_percent(interval=0.5) / psutil.cpu_count(), 2)
|
||||||
|
|
||||||
# this is a faster way of getting data for a process
|
# this is a faster way of getting data for a process
|
||||||
with p.oneshot():
|
with p.oneshot():
|
||||||
process_stats = {
|
process_stats = {
|
||||||
"cpu_usage": real_cpu,
|
"cpu_usage": real_cpu,
|
||||||
"memory_usage": Helpers.human_readable_file_size(
|
"memory_usage": Helpers.human_readable_file_size(p.memory_info()[0]),
|
||||||
p.memory_info()[0]
|
"mem_percentage": round(p.memory_percent(), 0),
|
||||||
),
|
}
|
||||||
"mem_percentage": round(p.memory_percent(), 0),
|
return process_stats
|
||||||
}
|
|
||||||
return process_stats
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(
|
|
||||||
f"Unable to get process details for pid: {process_pid} Error: {e}"
|
|
||||||
)
|
|
||||||
return {"cpu_usage": 0, "memory_usage": 0, "mem_percentage": 0}
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _try_all_disk_usage():
|
def _try_all_disk_usage():
|
||||||
try:
|
try:
|
||||||
return Stats._all_disk_usage()
|
return Stats._all_disk_usage()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"error while getting disk data due to {e}")
|
logger.debug(
|
||||||
|
"getting disk stats failed due to the following error:", exc_info=e
|
||||||
|
)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Source: https://github.com/giampaolo/psutil/blob/master/scripts/disk_usage.py
|
# Source: https://github.com/giampaolo/psutil/blob/master/scripts/disk_usage.py
|
||||||
@ -212,7 +218,7 @@ class Stats:
|
|||||||
|
|
||||||
return level_total_size
|
return level_total_size
|
||||||
|
|
||||||
def get_server_players(self, server_id): # pylint: disable=no-self-use
|
def get_server_players(self, server_id):
|
||||||
|
|
||||||
server = HelperServers.get_server_data_by_id(server_id)
|
server = HelperServers.get_server_data_by_id(server_id)
|
||||||
|
|
||||||
@ -246,23 +252,37 @@ class Stats:
|
|||||||
online_stats = json.loads(ping_obj.players)
|
online_stats = json.loads(ping_obj.players)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.info(f"Unable to read json from ping_obj: {e}")
|
logger.info(
|
||||||
|
"Unable to read json from ping_obj due to the following error:",
|
||||||
|
exc_info=e,
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
server_icon = base64.encodebytes(ping_obj.icon)
|
server_icon = base64.encodebytes(ping_obj.icon)
|
||||||
server_icon = server_icon.decode("utf-8")
|
server_icon = server_icon.decode("utf-8")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
server_icon = False
|
server_icon = False
|
||||||
logger.info(f"Unable to read the server icon : {e}")
|
logger.info(
|
||||||
|
"Unable to read the server icon due to the following error:", exc_info=e
|
||||||
ping_data = {
|
)
|
||||||
"online": online_stats.get("online", 0),
|
if ping_obj:
|
||||||
"max": online_stats.get("max", 0),
|
ping_data = {
|
||||||
"players": online_stats.get("players", 0),
|
"online": online_stats.get("online", 0),
|
||||||
"server_description": ping_obj.description,
|
"max": online_stats.get("max", 0),
|
||||||
"server_version": ping_obj.version,
|
"players": online_stats.get("players", 0),
|
||||||
"server_icon": server_icon,
|
"server_description": ping_obj.description,
|
||||||
}
|
"server_version": ping_obj.version,
|
||||||
|
"server_icon": server_icon,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
ping_data = {
|
||||||
|
"online": online_stats.get("online", 0),
|
||||||
|
"max": online_stats.get("max", 0),
|
||||||
|
"players": online_stats.get("players", 0),
|
||||||
|
"server_description": "",
|
||||||
|
"server_version": "",
|
||||||
|
"server_icon": server_icon,
|
||||||
|
}
|
||||||
|
|
||||||
return ping_data
|
return ping_data
|
||||||
|
|
||||||
@ -273,7 +293,9 @@ 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(f"Unable to read the server icon : {e}")
|
logger.info(
|
||||||
|
"Unable to read the server icon due to the following error:", exc_info=e
|
||||||
|
)
|
||||||
ping_data = {
|
ping_data = {
|
||||||
"online": ping_obj["server_player_count"],
|
"online": ping_obj["server_player_count"],
|
||||||
"max": ping_obj["server_player_max"],
|
"max": ping_obj["server_player_max"],
|
||||||
|
@ -38,6 +38,16 @@ class AuditLog(BaseModel):
|
|||||||
table_name = "audit_log"
|
table_name = "audit_log"
|
||||||
|
|
||||||
|
|
||||||
|
# **********************************************************************************
|
||||||
|
# Crafty Settings Class
|
||||||
|
# **********************************************************************************
|
||||||
|
class CraftySettings(BaseModel):
|
||||||
|
secret_api_key = CharField(default="")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
table_name = "crafty_settings"
|
||||||
|
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Host_Stats Class
|
# Host_Stats Class
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
@ -118,6 +128,7 @@ class Backups(BaseModel):
|
|||||||
max_backups = IntegerField()
|
max_backups = IntegerField()
|
||||||
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)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = "backups"
|
table_name = "backups"
|
||||||
@ -231,6 +242,17 @@ class HelpersManagement:
|
|||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_secret_api_key(key):
|
||||||
|
CraftySettings.insert(secret_api_key=key).execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_secret_api_key():
|
||||||
|
settings = CraftySettings.select(CraftySettings.secret_api_key).where(
|
||||||
|
CraftySettings.id == 1
|
||||||
|
)
|
||||||
|
return settings[0].secret_api_key
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Schedules Methods
|
# Schedules Methods
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
@ -330,6 +352,7 @@ class HelpersManagement:
|
|||||||
"max_backups": row.max_backups,
|
"max_backups": row.max_backups,
|
||||||
"server_id": row.server_id_id,
|
"server_id": row.server_id_id,
|
||||||
"compress": row.compress,
|
"compress": row.compress,
|
||||||
|
"shutdown": row.shutdown,
|
||||||
}
|
}
|
||||||
except IndexError:
|
except IndexError:
|
||||||
conf = {
|
conf = {
|
||||||
@ -338,6 +361,7 @@ class HelpersManagement:
|
|||||||
"max_backups": 0,
|
"max_backups": 0,
|
||||||
"server_id": server_id,
|
"server_id": server_id,
|
||||||
"compress": False,
|
"compress": False,
|
||||||
|
"shutdown": False,
|
||||||
}
|
}
|
||||||
return conf
|
return conf
|
||||||
|
|
||||||
@ -348,6 +372,7 @@ class HelpersManagement:
|
|||||||
max_backups: int = None,
|
max_backups: int = None,
|
||||||
excluded_dirs: list = None,
|
excluded_dirs: list = None,
|
||||||
compress: bool = False,
|
compress: bool = False,
|
||||||
|
shutdown: bool = False,
|
||||||
):
|
):
|
||||||
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():
|
||||||
@ -359,6 +384,7 @@ class HelpersManagement:
|
|||||||
"max_backups": 0,
|
"max_backups": 0,
|
||||||
"server_id": server_id,
|
"server_id": server_id,
|
||||||
"compress": False,
|
"compress": False,
|
||||||
|
"shutdown": False,
|
||||||
}
|
}
|
||||||
new_row = True
|
new_row = True
|
||||||
if max_backups is not None:
|
if max_backups is not None:
|
||||||
@ -367,6 +393,7 @@ class HelpersManagement:
|
|||||||
dirs_to_exclude = ",".join(excluded_dirs)
|
dirs_to_exclude = ",".join(excluded_dirs)
|
||||||
conf["excluded_dirs"] = dirs_to_exclude
|
conf["excluded_dirs"] = dirs_to_exclude
|
||||||
conf["compress"] = compress
|
conf["compress"] = compress
|
||||||
|
conf["shutdown"] = shutdown
|
||||||
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:
|
||||||
|
@ -5,6 +5,7 @@ import jwt
|
|||||||
from jwt import PyJWTError
|
from jwt import PyJWTError
|
||||||
|
|
||||||
from app.classes.models.users import HelperUsers, ApiKeys
|
from app.classes.models.users import HelperUsers, ApiKeys
|
||||||
|
from app.classes.controllers.management_controller import ManagementController
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -13,11 +14,14 @@ class Authentication:
|
|||||||
def __init__(self, helper):
|
def __init__(self, helper):
|
||||||
self.helper = helper
|
self.helper = helper
|
||||||
self.secret = "my secret"
|
self.secret = "my secret"
|
||||||
self.secret = self.helper.get_setting("apikey_secret", None)
|
try:
|
||||||
|
self.secret = ManagementController.get_crafty_api_key()
|
||||||
if self.secret is None or self.secret == "random":
|
if self.secret == "":
|
||||||
|
self.secret = self.helper.random_string_generator(64)
|
||||||
|
ManagementController.set_crafty_api_key(str(self.secret))
|
||||||
|
except:
|
||||||
self.secret = self.helper.random_string_generator(64)
|
self.secret = self.helper.random_string_generator(64)
|
||||||
self.helper.set_setting("apikey_secret", self.secret)
|
ManagementController.set_crafty_api_key(str(self.secret))
|
||||||
|
|
||||||
def generate(self, user_id, extra=None):
|
def generate(self, user_id, extra=None):
|
||||||
if extra is None:
|
if extra is None:
|
||||||
|
@ -2,14 +2,22 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import logging
|
import logging
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import tempfile
|
||||||
|
import zipfile
|
||||||
from zipfile import ZipFile, ZIP_DEFLATED
|
from zipfile import ZipFile, ZIP_DEFLATED
|
||||||
|
|
||||||
|
from app.classes.shared.helpers import Helpers
|
||||||
|
from app.classes.shared.console import Console
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FileHelpers:
|
class FileHelpers:
|
||||||
allowed_quotes = ['"', "'", "`"]
|
allowed_quotes = ['"', "'", "`"]
|
||||||
|
|
||||||
|
def __init__(self, helper):
|
||||||
|
self.helper: Helpers = helper
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def del_dirs(path):
|
def del_dirs(path):
|
||||||
path = pathlib.Path(path)
|
path = pathlib.Path(path)
|
||||||
@ -82,7 +90,6 @@ class FileHelpers:
|
|||||||
f"Error backing up: {os.path.join(root, file)}!"
|
f"Error backing up: {os.path.join(root, file)}!"
|
||||||
f" - Error was: {e}"
|
f" - Error was: {e}"
|
||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -113,3 +120,173 @@ class FileHelpers:
|
|||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def make_compressed_backup(
|
||||||
|
self, path_to_destination, path_to_zip, excluded_dirs, server_id
|
||||||
|
):
|
||||||
|
# create a ZipFile object
|
||||||
|
path_to_destination += ".zip"
|
||||||
|
ex_replace = [p.replace("\\", "/") for p in excluded_dirs]
|
||||||
|
total_bytes = 0
|
||||||
|
dir_bytes = Helpers.get_dir_size(path_to_zip)
|
||||||
|
results = {
|
||||||
|
"percent": 0,
|
||||||
|
"total_files": self.helper.human_readable_file_size(dir_bytes),
|
||||||
|
}
|
||||||
|
self.helper.websocket_helper.broadcast_page_params(
|
||||||
|
"/panel/server_detail",
|
||||||
|
{"id": str(server_id)},
|
||||||
|
"backup_status",
|
||||||
|
results,
|
||||||
|
)
|
||||||
|
with ZipFile(path_to_destination, "w", ZIP_DEFLATED) as zip_file:
|
||||||
|
for root, dirs, files in os.walk(path_to_zip, topdown=True):
|
||||||
|
for l_dir in dirs:
|
||||||
|
if str(os.path.join(root, l_dir)).replace("\\", "/") in ex_replace:
|
||||||
|
dirs.remove(l_dir)
|
||||||
|
ziproot = path_to_zip
|
||||||
|
for file in files:
|
||||||
|
if (
|
||||||
|
str(os.path.join(root, file)).replace("\\", "/")
|
||||||
|
not in ex_replace
|
||||||
|
and file != "crafty.sqlite"
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
logger.info(f"backing up: {os.path.join(root, file)}")
|
||||||
|
if os.name == "nt":
|
||||||
|
zip_file.write(
|
||||||
|
os.path.join(root, file),
|
||||||
|
os.path.join(root.replace(ziproot, ""), file),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
zip_file.write(
|
||||||
|
os.path.join(root, file),
|
||||||
|
os.path.join(root.replace(ziproot, "/"), file),
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(
|
||||||
|
f"Error backing up: {os.path.join(root, file)}!"
|
||||||
|
f" - Error was: {e}"
|
||||||
|
)
|
||||||
|
total_bytes += os.path.getsize(os.path.join(root, file))
|
||||||
|
percent = round((total_bytes / dir_bytes) * 100, 2)
|
||||||
|
results = {
|
||||||
|
"percent": percent,
|
||||||
|
"total_files": self.helper.human_readable_file_size(dir_bytes),
|
||||||
|
}
|
||||||
|
self.helper.websocket_helper.broadcast_page_params(
|
||||||
|
"/panel/server_detail",
|
||||||
|
{"id": str(server_id)},
|
||||||
|
"backup_status",
|
||||||
|
results,
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def make_backup(self, path_to_destination, path_to_zip, excluded_dirs, server_id):
|
||||||
|
# create a ZipFile object
|
||||||
|
path_to_destination += ".zip"
|
||||||
|
ex_replace = [p.replace("\\", "/") for p in excluded_dirs]
|
||||||
|
total_bytes = 0
|
||||||
|
dir_bytes = Helpers.get_dir_size(path_to_zip)
|
||||||
|
results = {
|
||||||
|
"percent": 0,
|
||||||
|
"total_files": self.helper.human_readable_file_size(dir_bytes),
|
||||||
|
}
|
||||||
|
self.helper.websocket_helper.broadcast_page_params(
|
||||||
|
"/panel/server_detail",
|
||||||
|
{"id": str(server_id)},
|
||||||
|
"backup_status",
|
||||||
|
results,
|
||||||
|
)
|
||||||
|
with ZipFile(path_to_destination, "w") as zip_file:
|
||||||
|
for root, dirs, files in os.walk(path_to_zip, topdown=True):
|
||||||
|
for l_dir in dirs:
|
||||||
|
if str(os.path.join(root, l_dir)).replace("\\", "/") in ex_replace:
|
||||||
|
dirs.remove(l_dir)
|
||||||
|
ziproot = path_to_zip
|
||||||
|
for file in files:
|
||||||
|
if (
|
||||||
|
str(os.path.join(root, file)).replace("\\", "/")
|
||||||
|
not in ex_replace
|
||||||
|
and file != "crafty.sqlite"
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
logger.info(f"backing up: {os.path.join(root, file)}")
|
||||||
|
if os.name == "nt":
|
||||||
|
zip_file.write(
|
||||||
|
os.path.join(root, file),
|
||||||
|
os.path.join(root.replace(ziproot, ""), file),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
zip_file.write(
|
||||||
|
os.path.join(root, file),
|
||||||
|
os.path.join(root.replace(ziproot, "/"), file),
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(
|
||||||
|
f"Error backing up: {os.path.join(root, file)}!"
|
||||||
|
f" - Error was: {e}"
|
||||||
|
)
|
||||||
|
total_bytes += os.path.getsize(os.path.join(root, file))
|
||||||
|
percent = round((total_bytes / dir_bytes) * 100, 2)
|
||||||
|
results = {
|
||||||
|
"percent": percent,
|
||||||
|
"total_files": self.helper.human_readable_file_size(dir_bytes),
|
||||||
|
}
|
||||||
|
self.helper.websocket_helper.broadcast_page_params(
|
||||||
|
"/panel/server_detail",
|
||||||
|
{"id": str(server_id)},
|
||||||
|
"backup_status",
|
||||||
|
results,
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unzip_file(zip_path):
|
||||||
|
new_dir_list = zip_path.split("/")
|
||||||
|
new_dir = ""
|
||||||
|
for i in range(len(new_dir_list) - 1):
|
||||||
|
if i == 0:
|
||||||
|
new_dir += new_dir_list[i]
|
||||||
|
else:
|
||||||
|
new_dir += "/" + new_dir_list[i]
|
||||||
|
|
||||||
|
if Helpers.check_file_perms(zip_path) and os.path.isfile(zip_path):
|
||||||
|
Helpers.ensure_dir_exists(new_dir)
|
||||||
|
temp_dir = tempfile.mkdtemp()
|
||||||
|
try:
|
||||||
|
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
||||||
|
zip_ref.extractall(temp_dir)
|
||||||
|
for i in enumerate(zip_ref.filelist):
|
||||||
|
if len(zip_ref.filelist) > 1 or not zip_ref.filelist[
|
||||||
|
i
|
||||||
|
].filename.endswith("/"):
|
||||||
|
break
|
||||||
|
|
||||||
|
full_root_path = temp_dir
|
||||||
|
|
||||||
|
for item in os.listdir(full_root_path):
|
||||||
|
if os.path.isdir(os.path.join(full_root_path, item)):
|
||||||
|
try:
|
||||||
|
FileHelpers.move_dir(
|
||||||
|
os.path.join(full_root_path, item),
|
||||||
|
os.path.join(new_dir, item),
|
||||||
|
)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.error(f"ERROR IN ZIP IMPORT: {ex}")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
FileHelpers.move_file(
|
||||||
|
os.path.join(full_root_path, item),
|
||||||
|
os.path.join(new_dir, item),
|
||||||
|
)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.error(f"ERROR IN ZIP IMPORT: {ex}")
|
||||||
|
except Exception as ex:
|
||||||
|
Console.error(ex)
|
||||||
|
else:
|
||||||
|
return "false"
|
||||||
|
return
|
||||||
|
@ -15,6 +15,8 @@ import html
|
|||||||
import zipfile
|
import zipfile
|
||||||
import pathlib
|
import pathlib
|
||||||
import ctypes
|
import ctypes
|
||||||
|
import subprocess
|
||||||
|
import itertools
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from socket import gethostname
|
from socket import gethostname
|
||||||
from contextlib import redirect_stderr, suppress
|
from contextlib import redirect_stderr, suppress
|
||||||
@ -22,7 +24,6 @@ from contextlib import redirect_stderr, suppress
|
|||||||
from app.classes.shared.null_writer import NullWriter
|
from app.classes.shared.null_writer import NullWriter
|
||||||
from app.classes.shared.console import Console
|
from app.classes.shared.console import Console
|
||||||
from app.classes.shared.installer import installer
|
from app.classes.shared.installer import installer
|
||||||
from app.classes.shared.file_helpers import FileHelpers
|
|
||||||
from app.classes.shared.translation import Translation
|
from app.classes.shared.translation import Translation
|
||||||
from app.classes.web.websocket_helper import WebSocketHelper
|
from app.classes.web.websocket_helper import WebSocketHelper
|
||||||
|
|
||||||
@ -81,6 +82,60 @@ class Helpers:
|
|||||||
print(f"Import Error: Unable to load {ex.name} module")
|
print(f"Import Error: Unable to load {ex.name} module")
|
||||||
installer.do_install()
|
installer.do_install()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def find_java_installs():
|
||||||
|
# If we're windows return oracle java versions,
|
||||||
|
# otherwise java vers need to be manual.
|
||||||
|
if os.name == "nt":
|
||||||
|
# Adapted from LeeKamentsky >>>
|
||||||
|
# https://github.com/LeeKamentsky/python-javabridge/blob/master/javabridge/locate.py
|
||||||
|
jdk_key_paths = (
|
||||||
|
"SOFTWARE\\JavaSoft\\JDK",
|
||||||
|
"SOFTWARE\\JavaSoft\\Java Development Kit",
|
||||||
|
)
|
||||||
|
java_paths = []
|
||||||
|
for jdk_key_path in jdk_key_paths:
|
||||||
|
try:
|
||||||
|
with suppress(OSError), winreg.OpenKey(
|
||||||
|
winreg.HKEY_LOCAL_MACHINE, jdk_key_path
|
||||||
|
) as kjdk:
|
||||||
|
for i in itertools.count():
|
||||||
|
version = winreg.EnumKey(kjdk, i)
|
||||||
|
kjdk_current = winreg.OpenKey(
|
||||||
|
winreg.HKEY_LOCAL_MACHINE,
|
||||||
|
jdk_key_path,
|
||||||
|
)
|
||||||
|
kjdk_current = winreg.OpenKey(
|
||||||
|
winreg.HKEY_LOCAL_MACHINE,
|
||||||
|
jdk_key_path + "\\" + version,
|
||||||
|
)
|
||||||
|
kjdk_current_values = dict( # pylint: disable=consider-using-dict-comprehension
|
||||||
|
[
|
||||||
|
winreg.EnumValue(kjdk_current, i)[:2]
|
||||||
|
for i in range(winreg.QueryInfoKey(kjdk_current)[1])
|
||||||
|
]
|
||||||
|
)
|
||||||
|
java_paths.append(kjdk_current_values["JavaHome"])
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno == 2:
|
||||||
|
continue
|
||||||
|
raise
|
||||||
|
return java_paths
|
||||||
|
|
||||||
|
# If we get here we're linux so we will use 'update-alternatives'
|
||||||
|
# (If distro does not have update-alternatives then manual input.)
|
||||||
|
try:
|
||||||
|
paths = subprocess.check_output(
|
||||||
|
["/usr/bin/update-alternatives", "--list", "java"], encoding="utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
if re.match("^(/[^/ ]*)+/?$", paths):
|
||||||
|
return paths.split("\n")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("Java Detect Error: ", e)
|
||||||
|
logger.error(f"Java Detect Error: {e}")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def float_to_string(gbs: float):
|
def float_to_string(gbs: float):
|
||||||
s = str(float(gbs) * 1000).rstrip("0").rstrip(".")
|
s = str(float(gbs) * 1000).rstrip("0").rstrip(".")
|
||||||
@ -443,53 +498,6 @@ class Helpers:
|
|||||||
return ctypes.windll.shell32.IsUserAnAdmin() == 1
|
return ctypes.windll.shell32.IsUserAnAdmin() == 1
|
||||||
return os.geteuid() == 0
|
return os.geteuid() == 0
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def unzip_file(zip_path):
|
|
||||||
new_dir_list = zip_path.split("/")
|
|
||||||
new_dir = ""
|
|
||||||
for i in range(len(new_dir_list) - 1):
|
|
||||||
if i == 0:
|
|
||||||
new_dir += new_dir_list[i]
|
|
||||||
else:
|
|
||||||
new_dir += "/" + new_dir_list[i]
|
|
||||||
|
|
||||||
if Helpers.check_file_perms(zip_path) and os.path.isfile(zip_path):
|
|
||||||
Helpers.ensure_dir_exists(new_dir)
|
|
||||||
temp_dir = tempfile.mkdtemp()
|
|
||||||
try:
|
|
||||||
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
|
||||||
zip_ref.extractall(temp_dir)
|
|
||||||
for i in enumerate(zip_ref.filelist):
|
|
||||||
if len(zip_ref.filelist) > 1 or not zip_ref.filelist[
|
|
||||||
i
|
|
||||||
].filename.endswith("/"):
|
|
||||||
break
|
|
||||||
|
|
||||||
full_root_path = temp_dir
|
|
||||||
|
|
||||||
for item in os.listdir(full_root_path):
|
|
||||||
if os.path.isdir(os.path.join(full_root_path, item)):
|
|
||||||
try:
|
|
||||||
FileHelpers.move_dir(
|
|
||||||
os.path.join(full_root_path, item),
|
|
||||||
os.path.join(new_dir, item),
|
|
||||||
)
|
|
||||||
except Exception as ex:
|
|
||||||
logger.error(f"ERROR IN ZIP IMPORT: {ex}")
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
FileHelpers.move_file(
|
|
||||||
os.path.join(full_root_path, item),
|
|
||||||
os.path.join(new_dir, item),
|
|
||||||
)
|
|
||||||
except Exception as ex:
|
|
||||||
logger.error(f"ERROR IN ZIP IMPORT: {ex}")
|
|
||||||
except Exception as ex:
|
|
||||||
Console.error(ex)
|
|
||||||
else:
|
|
||||||
return "false"
|
|
||||||
return
|
|
||||||
|
|
||||||
def ensure_logging_setup(self):
|
def ensure_logging_setup(self):
|
||||||
log_file = os.path.join(os.path.curdir, "logs", "commander.log")
|
log_file = os.path.join(os.path.curdir, "logs", "commander.log")
|
||||||
session_log_file = os.path.join(os.path.curdir, "logs", "session.log")
|
session_log_file = os.path.join(os.path.curdir, "logs", "session.log")
|
||||||
@ -837,7 +845,7 @@ class Helpers:
|
|||||||
for item in file_list:
|
for item in file_list:
|
||||||
if os.path.isdir(os.path.join(folder, item)):
|
if os.path.isdir(os.path.join(folder, item)):
|
||||||
dir_list.append(item)
|
dir_list.append(item)
|
||||||
else:
|
elif str(item) != "crafty.sqlite":
|
||||||
unsorted_files.append(item)
|
unsorted_files.append(item)
|
||||||
file_list = sorted(dir_list, key=str.casefold) + sorted(
|
file_list = sorted(dir_list, key=str.casefold) + sorted(
|
||||||
unsorted_files, key=str.casefold
|
unsorted_files, key=str.casefold
|
||||||
@ -868,13 +876,14 @@ class Helpers:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def generate_dir(folder, output=""):
|
def generate_dir(folder, output=""):
|
||||||
|
|
||||||
dir_list = []
|
dir_list = []
|
||||||
unsorted_files = []
|
unsorted_files = []
|
||||||
file_list = os.listdir(folder)
|
file_list = os.listdir(folder)
|
||||||
for item in file_list:
|
for item in file_list:
|
||||||
if os.path.isdir(os.path.join(folder, item)):
|
if os.path.isdir(os.path.join(folder, item)):
|
||||||
dir_list.append(item)
|
dir_list.append(item)
|
||||||
else:
|
elif str(item) != "crafty.sqlite":
|
||||||
unsorted_files.append(item)
|
unsorted_files.append(item)
|
||||||
file_list = sorted(dir_list, key=str.casefold) + sorted(
|
file_list = sorted(dir_list, key=str.casefold) + sorted(
|
||||||
unsorted_files, key=str.casefold
|
unsorted_files, key=str.casefold
|
||||||
@ -991,14 +1000,6 @@ class Helpers:
|
|||||||
[parent_path, child_path]
|
[parent_path, child_path]
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def copy_files(source, dest):
|
|
||||||
if os.path.isfile(source):
|
|
||||||
FileHelpers.copy_file(source, dest)
|
|
||||||
logger.info("Copying jar %s to %s", source, dest)
|
|
||||||
else:
|
|
||||||
logger.info("Source jar does not exist.")
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def download_file(executable_url, jar_path):
|
def download_file(executable_url, jar_path):
|
||||||
try:
|
try:
|
||||||
|
@ -34,8 +34,9 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class Controller:
|
class Controller:
|
||||||
def __init__(self, database, helper):
|
def __init__(self, database, helper, file_helper):
|
||||||
self.helper: Helpers = helper
|
self.helper: Helpers = helper
|
||||||
|
self.file_helper: FileHelpers = file_helper
|
||||||
self.server_jars: ServerJars = ServerJars(helper)
|
self.server_jars: ServerJars = ServerJars(helper)
|
||||||
self.users_helper: HelperUsers = HelperUsers(database, self.helper)
|
self.users_helper: HelperUsers = HelperUsers(database, self.helper)
|
||||||
self.roles_helper: HelperRoles = HelperRoles(database)
|
self.roles_helper: HelperRoles = HelperRoles(database)
|
||||||
@ -53,7 +54,7 @@ class Controller:
|
|||||||
)
|
)
|
||||||
self.server_perms: ServerPermsController = ServerPermsController()
|
self.server_perms: ServerPermsController = ServerPermsController()
|
||||||
self.servers: ServersController = ServersController(
|
self.servers: ServersController = ServersController(
|
||||||
self.helper, self.servers_helper, self.management_helper
|
self.helper, self.servers_helper, self.management_helper, self.file_helper
|
||||||
)
|
)
|
||||||
self.users: UsersController = UsersController(
|
self.users: UsersController = UsersController(
|
||||||
self.helper, self.users_helper, self.authentication
|
self.helper, self.users_helper, self.authentication
|
||||||
|
@ -17,8 +17,6 @@ class DatabaseBuilder:
|
|||||||
logger.info("Fresh Install Detected - Creating Default Settings")
|
logger.info("Fresh Install Detected - Creating Default Settings")
|
||||||
Console.info("Fresh Install Detected - Creating Default Settings")
|
Console.info("Fresh Install Detected - Creating Default Settings")
|
||||||
default_data = self.helper.find_default_password()
|
default_data = self.helper.find_default_password()
|
||||||
# Reset this value if the DB has been dumped
|
|
||||||
self.helper.set_setting("apikey_secret", "random")
|
|
||||||
|
|
||||||
username = default_data.get("username", "admin")
|
username = default_data.get("username", "admin")
|
||||||
password = default_data.get("password", "crafty")
|
password = default_data.get("password", "crafty")
|
||||||
|
@ -9,7 +9,6 @@ import threading
|
|||||||
import logging.config
|
import logging.config
|
||||||
import subprocess
|
import subprocess
|
||||||
import html
|
import html
|
||||||
import tempfile
|
|
||||||
|
|
||||||
# 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
|
||||||
@ -102,12 +101,14 @@ class ServerOutBuf:
|
|||||||
class ServerInstance:
|
class ServerInstance:
|
||||||
server_object: Servers
|
server_object: Servers
|
||||||
helper: Helpers
|
helper: Helpers
|
||||||
|
file_helper: FileHelpers
|
||||||
management_helper: HelpersManagement
|
management_helper: HelpersManagement
|
||||||
stats: Stats
|
stats: Stats
|
||||||
stats_helper: HelperServerStats
|
stats_helper: HelperServerStats
|
||||||
|
|
||||||
def __init__(self, server_id, helper, management_helper, stats):
|
def __init__(self, server_id, helper, management_helper, stats, file_helper):
|
||||||
self.helper = helper
|
self.helper = helper
|
||||||
|
self.file_helper = file_helper
|
||||||
self.management_helper = management_helper
|
self.management_helper = management_helper
|
||||||
# holders for our process
|
# holders for our process
|
||||||
self.process = None
|
self.process = None
|
||||||
@ -126,6 +127,7 @@ class ServerInstance:
|
|||||||
self.stats = stats
|
self.stats = stats
|
||||||
self.server_object = HelperServers.get_server_obj(self.server_id)
|
self.server_object = HelperServers.get_server_obj(self.server_id)
|
||||||
self.stats_helper = HelperServerStats(self.server_id)
|
self.stats_helper = HelperServerStats(self.server_id)
|
||||||
|
self.last_backup_failed = False
|
||||||
try:
|
try:
|
||||||
tz = get_localzone()
|
tz = get_localzone()
|
||||||
except ZoneInfoNotFoundError:
|
except ZoneInfoNotFoundError:
|
||||||
@ -239,18 +241,23 @@ class ServerInstance:
|
|||||||
"Detected nebulous java in start command. "
|
"Detected nebulous java in start command. "
|
||||||
"Replacing with full java path."
|
"Replacing with full java path."
|
||||||
)
|
)
|
||||||
which_java_raw = self.helper.which_java()
|
# Checks for Oracle Java. Only Oracle Java's helper will cause a re-exec.
|
||||||
java_path = which_java_raw + "\\bin\\java"
|
if "/Oracle/Java/" in str(shutil.which("java")):
|
||||||
if str(which_java_raw) != str(self.helper.get_servers_root_dir) or str(
|
logger.info(
|
||||||
self.helper.get_servers_root_dir
|
"Oracle Java detected. Changing start command to avoid re-exec."
|
||||||
) in str(which_java_raw):
|
|
||||||
self.server_command[0] = java_path
|
|
||||||
else:
|
|
||||||
logger.critcal(
|
|
||||||
"Possible attack detected. User attempted to exec "
|
|
||||||
"java binary from server directory."
|
|
||||||
)
|
)
|
||||||
return
|
which_java_raw = self.helper.which_java()
|
||||||
|
java_path = which_java_raw + "\\bin\\java"
|
||||||
|
if str(which_java_raw) != str(self.helper.get_servers_root_dir) or str(
|
||||||
|
self.helper.get_servers_root_dir
|
||||||
|
) in str(which_java_raw):
|
||||||
|
self.server_command[0] = java_path
|
||||||
|
else:
|
||||||
|
logger.critcal(
|
||||||
|
"Possible attack detected. User attempted to exec "
|
||||||
|
"java binary from server directory."
|
||||||
|
)
|
||||||
|
return
|
||||||
self.server_path = Helpers.get_os_understandable_path(self.settings["path"])
|
self.server_path = Helpers.get_os_understandable_path(self.settings["path"])
|
||||||
|
|
||||||
# let's do some quick checking to make sure things actually exists
|
# let's do some quick checking to make sure things actually exists
|
||||||
@ -840,6 +847,7 @@ class ServerInstance:
|
|||||||
"backup_reload",
|
"backup_reload",
|
||||||
{"percent": 0, "total_files": 0},
|
{"percent": 0, "total_files": 0},
|
||||||
)
|
)
|
||||||
|
was_server_running = None
|
||||||
logger.info(f"Starting server {self.name} (ID {self.server_id}) backup")
|
logger.info(f"Starting server {self.name} (ID {self.server_id}) backup")
|
||||||
server_users = PermissionsServers.get_server_user_list(self.server_id)
|
server_users = PermissionsServers.get_server_user_list(self.server_id)
|
||||||
for user in server_users:
|
for user in server_users:
|
||||||
@ -852,6 +860,15 @@ 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["shutdown"]:
|
||||||
|
logger.info(
|
||||||
|
"Found shutdown preference. Delaying"
|
||||||
|
+ "backup start. Shutting down server."
|
||||||
|
)
|
||||||
|
if self.check_running():
|
||||||
|
self.stop_server()
|
||||||
|
was_server_running = True
|
||||||
|
|
||||||
self.helper.ensure_dir_exists(self.settings["backup_path"])
|
self.helper.ensure_dir_exists(self.settings["backup_path"])
|
||||||
try:
|
try:
|
||||||
backup_filename = (
|
backup_filename = (
|
||||||
@ -863,62 +880,27 @@ class ServerInstance:
|
|||||||
f" (ID#{self.server_id}, path={self.server_path}) "
|
f" (ID#{self.server_id}, path={self.server_path}) "
|
||||||
f"at '{backup_filename}'"
|
f"at '{backup_filename}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
temp_dir = tempfile.mkdtemp()
|
|
||||||
self.server_scheduler.add_job(
|
|
||||||
self.backup_status,
|
|
||||||
"interval",
|
|
||||||
seconds=1,
|
|
||||||
id="backup_" + str(self.server_id),
|
|
||||||
args=[temp_dir + "/", backup_filename + ".zip"],
|
|
||||||
)
|
|
||||||
# pylint: disable=unexpected-keyword-arg
|
|
||||||
try:
|
|
||||||
FileHelpers.copy_dir(self.server_path, temp_dir, dirs_exist_ok=True)
|
|
||||||
except shutil.Error as e:
|
|
||||||
logger.error(f"Failed to fully complete backup due to shutil error {e}")
|
|
||||||
excluded_dirs = HelpersManagement.get_excluded_backup_dirs(self.server_id)
|
excluded_dirs = HelpersManagement.get_excluded_backup_dirs(self.server_id)
|
||||||
server_dir = Helpers.get_os_understandable_path(self.settings["path"])
|
server_dir = Helpers.get_os_understandable_path(self.settings["path"])
|
||||||
|
|
||||||
for my_dir in excluded_dirs:
|
|
||||||
# Take the full path of the excluded dir and replace the
|
|
||||||
# server path with the temp path, this is so that we're
|
|
||||||
# only deleting excluded dirs from the temp path
|
|
||||||
# and not the server path
|
|
||||||
excluded_dir = Helpers.get_os_understandable_path(my_dir).replace(
|
|
||||||
server_dir, Helpers.get_os_understandable_path(temp_dir)
|
|
||||||
)
|
|
||||||
# Next, check to see if it is a directory
|
|
||||||
if os.path.isdir(excluded_dir):
|
|
||||||
# If it is a directory,
|
|
||||||
# recursively delete the entire directory from the backup
|
|
||||||
try:
|
|
||||||
FileHelpers.del_dirs(excluded_dir)
|
|
||||||
except FileNotFoundError:
|
|
||||||
Console.error(
|
|
||||||
f"Excluded dir {excluded_dir} not found. Moving on..."
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# If not, just remove the file
|
|
||||||
try:
|
|
||||||
os.remove(excluded_dir)
|
|
||||||
except:
|
|
||||||
Console.error(
|
|
||||||
f"Excluded dir {excluded_dir} not found. Moving on..."
|
|
||||||
)
|
|
||||||
if conf["compress"]:
|
if conf["compress"]:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Found compress backup to be true. Calling compressed archive"
|
"Found compress backup to be true. Calling compressed archive"
|
||||||
)
|
)
|
||||||
FileHelpers.make_compressed_archive(
|
self.file_helper.make_compressed_backup(
|
||||||
Helpers.get_os_understandable_path(backup_filename), temp_dir
|
Helpers.get_os_understandable_path(backup_filename),
|
||||||
|
server_dir,
|
||||||
|
excluded_dirs,
|
||||||
|
self.server_id,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Found compress backup to be false. Calling NON-compressed archive"
|
"Found compress backup to be false. Calling NON-compressed archive"
|
||||||
)
|
)
|
||||||
FileHelpers.make_archive(
|
self.file_helper.make_backup(
|
||||||
Helpers.get_os_understandable_path(backup_filename), temp_dir
|
Helpers.get_os_understandable_path(backup_filename),
|
||||||
|
server_dir,
|
||||||
|
excluded_dirs,
|
||||||
|
self.server_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
while (
|
while (
|
||||||
@ -933,7 +915,6 @@ class ServerInstance:
|
|||||||
|
|
||||||
self.is_backingup = False
|
self.is_backingup = False
|
||||||
logger.info(f"Backup of server: {self.name} completed")
|
logger.info(f"Backup of server: {self.name} completed")
|
||||||
self.server_scheduler.remove_job("backup_" + str(self.server_id))
|
|
||||||
results = {"percent": 100, "total_files": 0, "current_file": 0}
|
results = {"percent": 100, "total_files": 0, "current_file": 0}
|
||||||
if len(self.helper.websocket_helper.clients) > 0:
|
if len(self.helper.websocket_helper.clients) > 0:
|
||||||
self.helper.websocket_helper.broadcast_page_params(
|
self.helper.websocket_helper.broadcast_page_params(
|
||||||
@ -953,12 +934,17 @@ class ServerInstance:
|
|||||||
HelperUsers.get_user_lang_by_id(user),
|
HelperUsers.get_user_lang_by_id(user),
|
||||||
).format(self.name),
|
).format(self.name),
|
||||||
)
|
)
|
||||||
|
if was_server_running:
|
||||||
|
logger.info(
|
||||||
|
"Backup complete. User had shutdown preference. Starting server."
|
||||||
|
)
|
||||||
|
self.start_server(HelperUsers.get_user_id_by_name("system"))
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
self.last_backup_failed = False
|
||||||
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})"
|
||||||
)
|
)
|
||||||
self.server_scheduler.remove_job("backup_" + str(self.server_id))
|
|
||||||
results = {"percent": 100, "total_files": 0, "current_file": 0}
|
results = {"percent": 100, "total_files": 0, "current_file": 0}
|
||||||
if len(self.helper.websocket_helper.clients) > 0:
|
if len(self.helper.websocket_helper.clients) > 0:
|
||||||
self.helper.websocket_helper.broadcast_page_params(
|
self.helper.websocket_helper.broadcast_page_params(
|
||||||
@ -968,8 +954,12 @@ class ServerInstance:
|
|||||||
results,
|
results,
|
||||||
)
|
)
|
||||||
self.is_backingup = False
|
self.is_backingup = False
|
||||||
finally:
|
if was_server_running:
|
||||||
FileHelpers.del_dirs(temp_dir)
|
logger.info(
|
||||||
|
"Backup complete. User had shutdown preference. Starting server."
|
||||||
|
)
|
||||||
|
self.start_server(HelperUsers.get_user_id_by_name("system"))
|
||||||
|
self.last_backup_failed = True
|
||||||
|
|
||||||
def backup_status(self, source_path, dest_path):
|
def backup_status(self, source_path, dest_path):
|
||||||
results = Helpers.calc_percent(source_path, dest_path)
|
results = Helpers.calc_percent(source_path, dest_path)
|
||||||
@ -982,6 +972,9 @@ class ServerInstance:
|
|||||||
results,
|
results,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def last_backup_status(self):
|
||||||
|
return self.last_backup_failed
|
||||||
|
|
||||||
def send_backup_status(self):
|
def send_backup_status(self):
|
||||||
try:
|
try:
|
||||||
return self.backup_stats
|
return self.backup_stats
|
||||||
@ -1087,7 +1080,7 @@ class ServerInstance:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# copies to backup dir
|
# copies to backup dir
|
||||||
Helpers.copy_files(current_executable, backup_executable)
|
FileHelpers.copy_file(current_executable, backup_executable)
|
||||||
|
|
||||||
# boolean returns true for false for success
|
# boolean returns true for false for success
|
||||||
downloaded = Helpers.download_file(
|
downloaded = Helpers.download_file(
|
||||||
@ -1249,7 +1242,7 @@ class ServerInstance:
|
|||||||
server_path = server["path"]
|
server_path = server["path"]
|
||||||
|
|
||||||
# process stats
|
# process stats
|
||||||
p_stats = Stats._try_get_process_stats(self.process)
|
p_stats = Stats._try_get_process_stats(self.process, self.check_running())
|
||||||
|
|
||||||
# TODO: search server properties file for possible override of 127.0.0.1
|
# TODO: search server properties file for possible override of 127.0.0.1
|
||||||
internal_ip = server["server_ip"]
|
internal_ip = server["server_ip"]
|
||||||
@ -1382,7 +1375,7 @@ class ServerInstance:
|
|||||||
server_path = server_dt["path"]
|
server_path = server_dt["path"]
|
||||||
|
|
||||||
# process stats
|
# process stats
|
||||||
p_stats = Stats._try_get_process_stats(self.process)
|
p_stats = Stats._try_get_process_stats(self.process, self.check_running())
|
||||||
|
|
||||||
# TODO: search server properties file for possible override of 127.0.0.1
|
# TODO: search server properties file for possible override of 127.0.0.1
|
||||||
# internal_ip = server['server_ip']
|
# internal_ip = server['server_ip']
|
||||||
|
@ -220,7 +220,7 @@ class FileHandler(BaseHandler):
|
|||||||
path = Helpers.get_os_understandable_path(self.get_argument("path", None))
|
path = Helpers.get_os_understandable_path(self.get_argument("path", None))
|
||||||
if Helpers.is_os_windows():
|
if Helpers.is_os_windows():
|
||||||
path = Helpers.wtol_path(path)
|
path = Helpers.wtol_path(path)
|
||||||
Helpers.unzip_file(path)
|
FileHelpers.unzip_file(path)
|
||||||
self.redirect(f"/panel/server_detail?id={server_id}&subpage=files")
|
self.redirect(f"/panel/server_detail?id={server_id}&subpage=files")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import typing as t
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
import shlex
|
||||||
import bleach
|
import bleach
|
||||||
import libgravatar
|
import libgravatar
|
||||||
import requests
|
import requests
|
||||||
@ -183,6 +184,7 @@ class PanelHandler(BaseHandler):
|
|||||||
logger.debug(f'User {exec_user["user_id"]} does not have permission')
|
logger.debug(f'User {exec_user["user_id"]} does not have permission')
|
||||||
self.redirect("/panel/error?error=Invalid Server ID")
|
self.redirect("/panel/error?error=Invalid Server ID")
|
||||||
return None
|
return None
|
||||||
|
return server_id
|
||||||
|
|
||||||
# Server fetching, spawned asynchronously
|
# Server fetching, spawned asynchronously
|
||||||
# TODO: Make the related front-end elements update with AJAX
|
# TODO: Make the related front-end elements update with AJAX
|
||||||
@ -496,6 +498,10 @@ class PanelHandler(BaseHandler):
|
|||||||
if server_id is None:
|
if server_id is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
server_obj = self.controller.servers.get_server_instance_by_id(server_id)
|
||||||
|
page_data["backup_failed"] = server_obj.last_backup_status()
|
||||||
|
server_obj = None
|
||||||
|
|
||||||
valid_subpages = [
|
valid_subpages = [
|
||||||
"term",
|
"term",
|
||||||
"logs",
|
"logs",
|
||||||
@ -626,6 +632,18 @@ class PanelHandler(BaseHandler):
|
|||||||
"/panel/error?error=Unauthorized access Server Config"
|
"/panel/error?error=Unauthorized access Server Config"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
page_data["java_versions"] = Helpers.find_java_installs()
|
||||||
|
server_obj: Servers = self.controller.servers.get_server_obj(server_id)
|
||||||
|
page_java = []
|
||||||
|
page_data["java_versions"].append("java")
|
||||||
|
for version in page_data["java_versions"]:
|
||||||
|
if os.name == "nt":
|
||||||
|
page_java.append(version)
|
||||||
|
else:
|
||||||
|
if len(version) > 0:
|
||||||
|
page_java.append(version)
|
||||||
|
|
||||||
|
page_data["java_versions"] = page_java
|
||||||
|
|
||||||
if subpage == "files":
|
if subpage == "files":
|
||||||
if (
|
if (
|
||||||
@ -1057,6 +1075,11 @@ class PanelHandler(BaseHandler):
|
|||||||
if user_id is None:
|
if user_id is None:
|
||||||
self.redirect("/panel/error?error=Invalid User ID")
|
self.redirect("/panel/error?error=Invalid User ID")
|
||||||
return
|
return
|
||||||
|
if int(user_id) != exec_user["user_id"] and not exec_user["superuser"]:
|
||||||
|
self.redirect(
|
||||||
|
"/panel/error?error=You are not authorized to view this page."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
template = "panel/panel_edit_user_apikeys.html"
|
template = "panel/panel_edit_user_apikeys.html"
|
||||||
|
|
||||||
@ -1221,6 +1244,9 @@ class PanelHandler(BaseHandler):
|
|||||||
self.download_file(name, file)
|
self.download_file(name, file)
|
||||||
self.redirect(f"/panel/server_detail?id={server_id}&subpage=files")
|
self.redirect(f"/panel/server_detail?id={server_id}&subpage=files")
|
||||||
|
|
||||||
|
elif page == "wiki":
|
||||||
|
template = "panel/wiki.html"
|
||||||
|
|
||||||
elif page == "download_support_package":
|
elif page == "download_support_package":
|
||||||
temp_zip_storage = exec_user["support_logs"]
|
temp_zip_storage = exec_user["support_logs"]
|
||||||
|
|
||||||
@ -1333,6 +1359,8 @@ class PanelHandler(BaseHandler):
|
|||||||
if Helpers.is_os_windows():
|
if Helpers.is_os_windows():
|
||||||
log_path.replace(" ", "^ ")
|
log_path.replace(" ", "^ ")
|
||||||
log_path = Helpers.wtol_path(log_path)
|
log_path = Helpers.wtol_path(log_path)
|
||||||
|
if not self.helper.validate_traversal(server_obj.path, log_path):
|
||||||
|
log_path = ""
|
||||||
executable = self.get_argument("executable", None)
|
executable = self.get_argument("executable", None)
|
||||||
execution_command = self.get_argument("execution_command", None)
|
execution_command = self.get_argument("execution_command", None)
|
||||||
server_ip = self.get_argument("server_ip", None)
|
server_ip = self.get_argument("server_ip", None)
|
||||||
@ -1346,11 +1374,46 @@ class PanelHandler(BaseHandler):
|
|||||||
auto_start = int(float(self.get_argument("auto_start", "0")))
|
auto_start = int(float(self.get_argument("auto_start", "0")))
|
||||||
crash_detection = int(float(self.get_argument("crash_detection", "0")))
|
crash_detection = int(float(self.get_argument("crash_detection", "0")))
|
||||||
logs_delete_after = int(float(self.get_argument("logs_delete_after", "0")))
|
logs_delete_after = int(float(self.get_argument("logs_delete_after", "0")))
|
||||||
|
java_selection = self.get_argument("java_selection", None)
|
||||||
# subpage = self.get_argument('subpage', None)
|
# subpage = self.get_argument('subpage', None)
|
||||||
|
|
||||||
server_id = self.check_server_id()
|
server_id = self.check_server_id()
|
||||||
if server_id is None:
|
if server_id is None:
|
||||||
return
|
return
|
||||||
|
if java_selection:
|
||||||
|
try:
|
||||||
|
execution_list = shlex.split(execution_command)
|
||||||
|
except ValueError:
|
||||||
|
self.redirect(
|
||||||
|
"/panel/error?error=Invalid execution command. Java path"
|
||||||
|
" must be surrounded by quotes."
|
||||||
|
" (Are you missing a closing quote?)"
|
||||||
|
)
|
||||||
|
if not any(
|
||||||
|
java_selection in path for path in Helpers.find_java_installs()
|
||||||
|
):
|
||||||
|
self.redirect(
|
||||||
|
"/panel/error?error=Attack attempted."
|
||||||
|
+ " A copy of this report is being sent to server owner."
|
||||||
|
)
|
||||||
|
self.controller.management.add_to_audit_log_raw(
|
||||||
|
exec_user["username"],
|
||||||
|
exec_user["user_id"],
|
||||||
|
server_id,
|
||||||
|
f"Attempted to send bad java path for {server_id}."
|
||||||
|
+ " Possible attack. Act accordingly.",
|
||||||
|
self.get_remote_ip(),
|
||||||
|
)
|
||||||
|
if java_selection != "java":
|
||||||
|
if self.helper.is_os_windows():
|
||||||
|
execution_list[0] = '"' + java_selection + '/bin/java"'
|
||||||
|
else:
|
||||||
|
execution_list[0] = '"' + java_selection + '"'
|
||||||
|
else:
|
||||||
|
execution_list[0] = "java"
|
||||||
|
execution_command = ""
|
||||||
|
for item in execution_list:
|
||||||
|
execution_command += item + " "
|
||||||
|
|
||||||
server_obj: Servers = self.controller.servers.get_server_obj(server_id)
|
server_obj: Servers = self.controller.servers.get_server_obj(server_id)
|
||||||
stale_executable = server_obj.executable
|
stale_executable = server_obj.executable
|
||||||
@ -1380,7 +1443,7 @@ class PanelHandler(BaseHandler):
|
|||||||
server_obj.path = server_obj.path
|
server_obj.path = server_obj.path
|
||||||
server_obj.log_path = server_obj.log_path
|
server_obj.log_path = server_obj.log_path
|
||||||
server_obj.executable = server_obj.executable
|
server_obj.executable = server_obj.executable
|
||||||
server_obj.execution_command = server_obj.execution_command
|
server_obj.execution_command = execution_command
|
||||||
server_obj.server_ip = server_obj.server_ip
|
server_obj.server_ip = server_obj.server_ip
|
||||||
server_obj.server_port = server_obj.server_port
|
server_obj.server_port = server_obj.server_port
|
||||||
server_obj.executable_update_url = server_obj.executable_update_url
|
server_obj.executable_update_url = server_obj.executable_update_url
|
||||||
@ -1424,6 +1487,7 @@ class PanelHandler(BaseHandler):
|
|||||||
|
|
||||||
server_obj = self.controller.servers.get_server_obj(server_id)
|
server_obj = self.controller.servers.get_server_obj(server_id)
|
||||||
compress = self.get_argument("compress", False)
|
compress = self.get_argument("compress", False)
|
||||||
|
shutdown = self.get_argument("shutdown", False)
|
||||||
check_changed = self.get_argument("changed")
|
check_changed = self.get_argument("changed")
|
||||||
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")
|
||||||
@ -1446,6 +1510,7 @@ class PanelHandler(BaseHandler):
|
|||||||
max_backups=max_backups,
|
max_backups=max_backups,
|
||||||
excluded_dirs=checked,
|
excluded_dirs=checked,
|
||||||
compress=bool(compress),
|
compress=bool(compress),
|
||||||
|
shutdown=bool(shutdown),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.controller.management.add_to_audit_log(
|
self.controller.management.add_to_audit_log(
|
||||||
@ -1892,6 +1957,13 @@ class PanelHandler(BaseHandler):
|
|||||||
self.redirect("/panel/error?error=Invalid User ID")
|
self.redirect("/panel/error?error=Invalid User ID")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if str(user_id) != str(exec_user["user_id"]) and not exec_user["superuser"]:
|
||||||
|
self.redirect(
|
||||||
|
"/panel/error?error=You do not have access to change"
|
||||||
|
+ "this user's api key."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
crafty_permissions_mask = self.get_perms()
|
crafty_permissions_mask = self.get_perms()
|
||||||
server_permissions_mask = self.get_perms_server()
|
server_permissions_mask = self.get_perms_server()
|
||||||
|
|
||||||
@ -1925,6 +1997,15 @@ class PanelHandler(BaseHandler):
|
|||||||
self.redirect("/panel/error?error=Invalid Key ID")
|
self.redirect("/panel/error?error=Invalid Key ID")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if (
|
||||||
|
str(key.user_id) != str(exec_user["user_id"])
|
||||||
|
and not exec_user["superuser"]
|
||||||
|
):
|
||||||
|
self.redirect(
|
||||||
|
"/panel/error?error=You are not authorized to access this key."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
self.controller.management.add_to_audit_log(
|
self.controller.management.add_to_audit_log(
|
||||||
exec_user["user_id"],
|
exec_user["user_id"],
|
||||||
f"Generated a new API token for the key {key.name} "
|
f"Generated a new API token for the key {key.name} "
|
||||||
@ -2141,6 +2222,15 @@ class PanelHandler(BaseHandler):
|
|||||||
self.redirect("/panel/error?error=Invalid Key ID")
|
self.redirect("/panel/error?error=Invalid Key ID")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
key_obj = self.controller.users.get_user_api_key(key_id)
|
||||||
|
|
||||||
|
if key_obj.user_id != exec_user["user_id"] and not exec_user["superuser"]:
|
||||||
|
self.redirect(
|
||||||
|
"/panel/error?error=You do not have access to change"
|
||||||
|
+ "this user's api key."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
self.controller.users.delete_user_api_key(key_id)
|
self.controller.users.delete_user_api_key(key_id)
|
||||||
|
|
||||||
self.controller.management.add_to_audit_log(
|
self.controller.management.add_to_audit_log(
|
||||||
@ -2150,7 +2240,8 @@ class PanelHandler(BaseHandler):
|
|||||||
server_id=0,
|
server_id=0,
|
||||||
source_ip=self.get_remote_ip(),
|
source_ip=self.get_remote_ip(),
|
||||||
)
|
)
|
||||||
self.redirect("/panel/panel_config")
|
self.finish()
|
||||||
|
self.redirect(f"/panel/edit_user_apikeys?id={key_obj.user_id}")
|
||||||
else:
|
else:
|
||||||
self.set_status(404)
|
self.set_status(404)
|
||||||
self.render(
|
self.render(
|
||||||
|
@ -17,6 +17,15 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class ServerHandler(BaseHandler):
|
class ServerHandler(BaseHandler):
|
||||||
|
def get_user_roles(self):
|
||||||
|
user_roles = {}
|
||||||
|
for user_id in self.controller.users.get_all_user_ids():
|
||||||
|
user_roles_list = self.controller.users.get_user_roles_names(user_id)
|
||||||
|
# user_servers =
|
||||||
|
# self.controller.servers.get_authorized_servers(user.user_id)
|
||||||
|
user_roles[user_id] = user_roles_list
|
||||||
|
return user_roles
|
||||||
|
|
||||||
@tornado.web.authenticated
|
@tornado.web.authenticated
|
||||||
def get(self, page):
|
def get(self, page):
|
||||||
(
|
(
|
||||||
@ -271,11 +280,19 @@ class ServerHandler(BaseHandler):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if page == "step1":
|
if page == "step1":
|
||||||
|
if not superuser and not self.controller.crafty_perms.can_create_server(
|
||||||
|
exec_user["user_id"]
|
||||||
|
):
|
||||||
|
self.redirect(
|
||||||
|
"/panel/error?error=Unauthorized access: "
|
||||||
|
"not a server creator or server limit reached"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
if not superuser:
|
if not superuser:
|
||||||
user_roles = self.controller.roles.get_all_roles()
|
user_roles = self.controller.roles.get_all_roles()
|
||||||
else:
|
else:
|
||||||
user_roles = self.controller.roles.get_all_roles()
|
user_roles = self.get_user_roles()
|
||||||
server = bleach.clean(self.get_argument("server", ""))
|
server = bleach.clean(self.get_argument("server", ""))
|
||||||
server_name = bleach.clean(self.get_argument("server_name", ""))
|
server_name = bleach.clean(self.get_argument("server_name", ""))
|
||||||
min_mem = bleach.clean(self.get_argument("min_memory", ""))
|
min_mem = bleach.clean(self.get_argument("min_memory", ""))
|
||||||
@ -396,6 +413,14 @@ class ServerHandler(BaseHandler):
|
|||||||
self.redirect("/panel/dashboard")
|
self.redirect("/panel/dashboard")
|
||||||
|
|
||||||
if page == "bedrock_step1":
|
if page == "bedrock_step1":
|
||||||
|
if not superuser and not self.controller.crafty_perms.can_create_server(
|
||||||
|
exec_user["user_id"]
|
||||||
|
):
|
||||||
|
self.redirect(
|
||||||
|
"/panel/error?error=Unauthorized access: "
|
||||||
|
"not a server creator or server limit reached"
|
||||||
|
)
|
||||||
|
return
|
||||||
if not superuser:
|
if not superuser:
|
||||||
user_roles = self.controller.roles.get_all_roles()
|
user_roles = self.controller.roles.get_all_roles()
|
||||||
else:
|
else:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"major": 4,
|
"major": 4,
|
||||||
"minor": 0,
|
"minor": 0,
|
||||||
"sub": 2,
|
"sub": 4,
|
||||||
"meta": "beta"
|
"meta": "beta"
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.sidebar > .nav > .nav-item:not(.nav-profile) > .nav-link:before {
|
.sidebar>.nav>.nav-item:not(.nav-profile)>.nav-link:before {
|
||||||
content: none;
|
content: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 30px;
|
left: 30px;
|
||||||
@ -21,7 +21,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar > .nav > .nav-item:not(.nav-profile) > .nav-link:before {
|
.sidebar>.nav>.nav-item:not(.nav-profile)>.nav-link:before {
|
||||||
content: none;
|
content: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 30px;
|
left: 30px;
|
||||||
@ -33,43 +33,48 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar > .nav .nav-item .nav-link, .collapsed{
|
.sidebar>.nav .nav-item .nav-link,
|
||||||
|
.collapsed {
|
||||||
padding: 15px 30px;
|
padding: 15px 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mc-log-time{
|
.mc-log-time {
|
||||||
color:#19d895;
|
color: #19d895;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mc-log-info{
|
.mc-log-info {
|
||||||
color:#8862e0;
|
color: #8862e0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mc-log-warn{
|
.mc-log-warn {
|
||||||
color:#ffaf00;
|
color: #ffaf00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mc-log-error{
|
.mc-log-error {
|
||||||
color:#af463f;
|
color: #af463f;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mc-log-fatal{
|
.mc-log-fatal {
|
||||||
color:#da0f00;
|
color: #da0f00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mc-log-keyword{
|
.mc-log-keyword {
|
||||||
color:#2196f3;
|
color: #2196f3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollable-element {
|
.scrollable-element {
|
||||||
scrollbar-color: red yellow;
|
scrollbar-color: red yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
.term-nav-item {
|
.term-nav-item {
|
||||||
padding: 1%;
|
padding: 1%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix body scrollbar color */
|
/* Fix body scrollbar color */
|
||||||
body { background-color: var(--dark) !important; /* Firefox */ }
|
body {
|
||||||
|
background-color: var(--dark) !important;
|
||||||
|
/* Firefox */
|
||||||
|
}
|
||||||
|
|
||||||
/* Webkit */
|
/* Webkit */
|
||||||
/* Didn't really work out
|
/* Didn't really work out
|
||||||
@ -81,11 +86,20 @@ body { background-color: var(--dark) !important; /* Firefox */ }
|
|||||||
::-webkit-scrollbar-track { background-color: #202538; }
|
::-webkit-scrollbar-track { background-color: #202538; }
|
||||||
::-webkit-scrollbar-corner { background-color: #202538; }*/
|
::-webkit-scrollbar-corner { background-color: #202538; }*/
|
||||||
|
|
||||||
.actions_serverlist > a > i {
|
.actions_serverlist>a>i {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.actions_serveritem {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.corner {
|
.corner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion .card {
|
||||||
|
margin-bottom: 0px;
|
||||||
}
|
}
|
@ -1,133 +1,144 @@
|
|||||||
<!-- partial -->
|
<!-- partial -->
|
||||||
<div class="container-fluid page-body-wrapper">
|
<div class="container-fluid page-body-wrapper">
|
||||||
<!-- partial:partials/_sidebar.html -->
|
<!-- partial:partials/_sidebar.html -->
|
||||||
<style>
|
<style>
|
||||||
@media screen and (max-width: 991px) {
|
@media screen and (max-width: 991px) {
|
||||||
.sidebar-offcanvas {
|
.sidebar-offcanvas {
|
||||||
-webkit-transition: all 0.25s cubic-bezier(.22,.61,.36,1);
|
-webkit-transition: all 0.25s cubic-bezier(.22, .61, .36, 1);
|
||||||
transition: all 0.25s cubic-bezier(.22,.61,.36,1);
|
transition: all 0.25s cubic-bezier(.22, .61, .36, 1);
|
||||||
box-shadow: 0px 8px 17px 2px rgba(0,0,0,0.14) , 0px 3px 14px 2px rgba(0,0,0,0.12) , 0px 5px 5px -3px rgba(0,0,0,0.2);
|
box-shadow: 0px 8px 17px 2px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12), 0px 5px 5px -3px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
function debounce(func, wait, immediate) {
|
function debounce(func, wait, immediate) {
|
||||||
var timeout;
|
var timeout;
|
||||||
return function() {
|
return function () {
|
||||||
var context = this, args = arguments;
|
var context = this, args = arguments;
|
||||||
var later = function() {
|
var later = function () {
|
||||||
timeout = null;
|
timeout = null;
|
||||||
if (!immediate) func.apply(context, args);
|
if (!immediate) func.apply(context, args);
|
||||||
};
|
};
|
||||||
var callNow = immediate && !timeout;
|
var callNow = immediate && !timeout;
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
timeout = setTimeout(later, wait);
|
timeout = setTimeout(later, wait);
|
||||||
if (callNow) func.apply(context, args);
|
if (callNow) func.apply(context, args);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
function isExtraLargeBreakpoint() {
|
function isExtraLargeBreakpoint() {
|
||||||
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
|
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
|
||||||
return vw >= 1200;
|
return vw >= 1200;
|
||||||
}
|
}
|
||||||
function isLargeBreakpoint() {
|
function isLargeBreakpoint() {
|
||||||
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
|
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
|
||||||
return vw >= 992;
|
return vw >= 992;
|
||||||
}
|
}
|
||||||
$(document).ready(function() {
|
$(document).ready(function () {
|
||||||
sidebarResizeHandler(null);
|
sidebarResizeHandler(null);
|
||||||
$(window).on(
|
$(window).on(
|
||||||
'resize',
|
'resize',
|
||||||
debounce(sidebarResizeHandler, 25, true)
|
debounce(sidebarResizeHandler, 25, true)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
function sidebarResizeHandler(e) {
|
function sidebarResizeHandler(e) {
|
||||||
/*
|
/*
|
||||||
Viewport sizes: Extra large (vw >= 1200px), large (vw >= 992px), medium (vw >= 768px)
|
Viewport sizes: Extra large (vw >= 1200px), large (vw >= 992px), medium (vw >= 768px)
|
||||||
- A localstorage item is set to remember a user's preference between collapsed or expanded.
|
- A localstorage item is set to remember a user's preference between collapsed or expanded.
|
||||||
- For extra large viewports, the sidebar is the user's preference (by default expanded). When
|
- For extra large viewports, the sidebar is the user's preference (by default expanded). When
|
||||||
expanded or collapsed manually, it doesn't overlap the page content and the preference
|
expanded or collapsed manually, it doesn't overlap the page content and the preference
|
||||||
gets saved to a localstorage item.
|
gets saved to a localstorage item.
|
||||||
- For large viewports, the sidebar is collapsed. When expanded manually, it doesn't overlap
|
- For large viewports, the sidebar is collapsed. When expanded manually, it doesn't overlap
|
||||||
the page content. The user's localstorage preference is not overridden during this state.
|
the page content. The user's localstorage preference is not overridden during this state.
|
||||||
- For medium and below viewports, the sidebar is hidden behing a hamburger icon. When expanded, the sidebar
|
- For medium and below viewports, the sidebar is hidden behing a hamburger icon. When expanded, the sidebar
|
||||||
overlaps the page content. The user's localstorage preference is not overridden during this state.
|
overlaps the page content. The user's localstorage preference is not overridden during this state.
|
||||||
|
|
||||||
More code in `app/frontend/static/assets/js/shared/misc.js` and `app/frontend/templates/base.html`
|
More code in `app/frontend/static/assets/js/shared/misc.js` and `app/frontend/templates/base.html`
|
||||||
*/
|
*/
|
||||||
if (isExtraLargeBreakpoint()) {
|
if (isExtraLargeBreakpoint()) {
|
||||||
let value = localStorage.getItem('crafty-sidebar-expanded') !== 'false';
|
let value = localStorage.getItem('crafty-sidebar-expanded') !== 'false';
|
||||||
$('body').toggleClass('sidebar-icon-only', !value);
|
$('body').toggleClass('sidebar-icon-only', !value);
|
||||||
localStorage.setItem('crafty-sidebar-expanded', value);
|
localStorage.setItem('crafty-sidebar-expanded', value);
|
||||||
} else if (isLargeBreakpoint()) {
|
} else if (isLargeBreakpoint()) {
|
||||||
$('body').toggleClass('sidebar-icon-only', true);
|
$('body').toggleClass('sidebar-icon-only', true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<nav class="sidebar sidebar-offcanvas" id="sidebar">
|
<nav class="sidebar sidebar-offcanvas" id="sidebar">
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
|
|
||||||
<li class="nav-item nav-category" style="margin-top:10px;">{{ translate('sidebar', 'navigation', data['lang']) }}</li>
|
<li class="nav-item nav-category" style="margin-top:10px;">{{ translate('sidebar', 'navigation', data['lang']) }}
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/panel/dashboard">
|
||||||
|
<i class="fas fa-chart-network"></i>
|
||||||
|
<span class="menu-title">{{ translate('sidebar', 'dashboard', data['lang']) }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" data-toggle="collapse" href="#page-layouts" aria-expanded="false"
|
||||||
|
aria-controls="page-layouts">
|
||||||
|
<i class="fas fa-server"></i>
|
||||||
|
<span class="menu-title">{{ translate('sidebar', 'servers', data['lang']) }}</span>
|
||||||
|
<i class="menu-arrow"></i>
|
||||||
|
</a>
|
||||||
|
<div class="collapse" id="page-layouts">
|
||||||
|
<ul class="nav flex-column sub-menu">
|
||||||
|
{% if data['crafty_permissions']['Server_Creation'] in data['user_crafty_permissions'] %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/panel/dashboard">
|
<a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> {{ translate('sidebar',
|
||||||
<i class="fas fa-chart-network"></i>
|
'newServer', data['lang']) }}</a>
|
||||||
<span class="menu-title">{{ translate('sidebar', 'dashboard', data['lang']) }}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
|
{% end %}
|
||||||
|
{% for s in data['menu_servers'] %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" data-toggle="collapse" href="#page-layouts" aria-expanded="false" aria-controls="page-layouts">
|
<a class="nav-link" href="/panel/server_detail?id={{s['server_id']}}"><i class="fas fa-server"></i>
|
||||||
<i class="fas fa-server"></i>
|
{{s['server_name']}}</a>
|
||||||
<span class="menu-title">{{ translate('sidebar', 'servers', data['lang']) }}</span>
|
|
||||||
<i class="menu-arrow"></i>
|
|
||||||
</a>
|
|
||||||
<div class="collapse" id="page-layouts">
|
|
||||||
<ul class="nav flex-column sub-menu">
|
|
||||||
{% if data['crafty_permissions']['Server_Creation'] in data['user_crafty_permissions'] %}
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> {{ translate('sidebar', 'newServer', data['lang']) }}</a>
|
|
||||||
</li>
|
|
||||||
{% end %}
|
|
||||||
{% for s in data['menu_servers'] %}
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="/panel/server_detail?id={{s['server_id']}}"><i class="fas fa-server"></i> {{s['server_name']}}</a>
|
|
||||||
</li>
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="https://wiki.craftycontrol.com" target="_blank">
|
|
||||||
<i class="fas fa-book"></i>
|
|
||||||
<span class="menu-title">{{ translate('sidebar', 'documentation', data['lang']) }}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="https://discord.gg/9VJPhCE" target="_blank">
|
|
||||||
<i class="fab fa-discord"></i>
|
|
||||||
<span class="menu-title">Discord</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="/panel/credits">
|
|
||||||
<i class="fas fa-heart"></i>
|
|
||||||
<span class="menu-title">{{ translate('sidebar', 'credits', data['lang']) }}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
{% if data['show_contribute'] %}
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="/panel/contribute">
|
|
||||||
<i class="fas fa-donate"></i>
|
|
||||||
<span class="menu-title">{{ translate('sidebar', 'contribute', data['lang']) }}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</div>
|
||||||
<!-- partial -->
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="https://wiki.craftycontrol.com" target="_blank">
|
||||||
|
<i class="fas fa-book"></i>
|
||||||
|
<span class="menu-title">{{ translate('sidebar', 'documentation', data['lang']) }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/panel/wiki">
|
||||||
|
<i class="fa fa-info-circle"></i>
|
||||||
|
<span class="menu-title">Wiki</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="https://discord.gg/9VJPhCE" target="_blank">
|
||||||
|
<i class="fab fa-discord"></i>
|
||||||
|
<span class="menu-title">Discord</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/panel/credits">
|
||||||
|
<i class="fas fa-heart"></i>
|
||||||
|
<span class="menu-title">{{ translate('sidebar', 'credits', data['lang']) }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{% if data['show_contribute'] %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/panel/contribute">
|
||||||
|
<i class="fas fa-donate"></i>
|
||||||
|
<span class="menu-title">{{ translate('sidebar', 'contribute', data['lang']) }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<!-- partial -->
|
@ -25,21 +25,21 @@
|
|||||||
<!-- Page Title Header Ends-->
|
<!-- Page Title Header Ends-->
|
||||||
{% if data['first_log'] %}
|
{% if data['first_log'] %}
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function () {
|
||||||
bootbox.alert({
|
bootbox.alert({
|
||||||
backdrop: true,
|
backdrop: true,
|
||||||
title: 'Your Feedback Is Appreciated...',
|
title: 'Your Feedback Is Appreciated...',
|
||||||
message: '<p>We will only request this information from the admin user once... 😊</p>'
|
message: '<p>We will only request this information from the admin user once... 😊</p>'
|
||||||
+'<p><strong>All data collected is completely anonymous</strong> and is only used to improve Crafty 4.0 and allow us to more accurately report the number of Crafty 4 users.</p><iframe width="640px" height="480px"'
|
+ '<p><strong>All data collected is completely anonymous</strong> and is only used to improve Crafty 4.0 and allow us to more accurately report the number of Crafty 4 users.</p><iframe width="640px" height="480px"'
|
||||||
+'src="https://forms.office.com/Pages/ResponsePage.aspx?id=LwLajNkpXU2CKc95G1oO3MN0Hu3oEUNLr-EtLx31TS5UNUNVQlFNVUVYMEc'
|
+ 'src="https://forms.office.com/Pages/ResponsePage.aspx?id=LwLajNkpXU2CKc95G1oO3MN0Hu3oEUNLr-EtLx31TS5UNUNVQlFNVUVYMEc'
|
||||||
+'1V1BKS0FQUUlERUtWQy4u&embed=true" frameborder="0" marginwidth="0" marginheight="0" style="border: none; max-width:100%;'
|
+ '1V1BKS0FQUUlERUtWQy4u&embed=true" frameborder="0" marginwidth="0" marginheight="0" style="border: none; max-width:100%;'
|
||||||
+' max-height:100vh" allowfullscreen webkitallowfullscreen mozallowfullscreen msallowfullscreen> </iframe>',
|
+ ' max-height:100vh" allowfullscreen webkitallowfullscreen mozallowfullscreen msallowfullscreen> </iframe>',
|
||||||
buttons: {
|
buttons: {
|
||||||
ok: {
|
ok: {
|
||||||
label: 'Skip Survey/Done',
|
label: 'Skip Survey/Done',
|
||||||
className: 'btn-secondary'
|
className: 'btn-secondary'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -60,12 +60,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="wrapper my-auto ml-auto ml-lg-4">
|
<div class="wrapper my-auto ml-auto ml-lg-4">
|
||||||
<p id="cpu_data" class="mb-0 text-success" data-toggle="tooltip" data-placement="top" data-html="true"
|
<p id="cpu_data" class="mb-0 text-success" data-toggle="tooltip" data-placement="top" data-html="true"
|
||||||
title="{% raw translate('dashboard', 'cpuCores', data['lang']) %}: {{ data.get('hosts_data').get('cpu_cores') }} <br /> {% raw translate('dashboard', 'cpuCurFreq', data['lang']) %}: {{ data.get('hosts_data').get('cpu_cur_freq') }} <br /> {% raw translate('dashboard', 'cpuMaxFreq', data['lang']) %}: {{ data.get('hosts_data').get('cpu_max_freq') }}">
|
title="{% raw translate('dashboard', 'cpuCores', data['lang']) %}: {{ data.get('hosts_data').get('cpu_cores') }} <br /> {% raw translate('dashboard', 'cpuCurFreq', data['lang']) %}: {{ data.get('hosts_data').get('cpu_cur_freq') }} <br /> {% raw translate('dashboard', 'cpuMaxFreq', data['lang']) %}: {{ data.get('hosts_data').get('cpu_max_freq') }}">
|
||||||
{{ translate('dashboard', 'cpuUsage', data['lang']) }}: <span id="cpu_usage">{{
|
{{ translate('dashboard', 'cpuUsage', data['lang']) }}: <span id="cpu_usage">{{
|
||||||
data.get('hosts_data').get('cpu_usage') }}</span>
|
data.get('hosts_data').get('cpu_usage') }}</span>
|
||||||
</p>
|
</p>
|
||||||
<p id="mem_usage" class="mb-0 text-danger" data-toggle="tooltip" data-placement="top"
|
<p id="mem_usage" class="mb-0 text-danger" data-toggle="tooltip" data-placement="top"
|
||||||
title="{{ translate('dashboard', 'memUsage', data['lang']) }}: {{ data.get('hosts_data').get('mem_usage') }}">
|
title="{{ translate('dashboard', 'memUsage', data['lang']) }}: {{ data.get('hosts_data').get('mem_usage') }}">
|
||||||
{{ translate('dashboard', 'memUsage', data['lang']) }}: <span id="mem_percent">{{
|
{{ translate('dashboard', 'memUsage', data['lang']) }}: <span id="mem_percent">{{
|
||||||
data.get('hosts_data').get('mem_percent') }}%</span>
|
data.get('hosts_data').get('mem_percent') }}%</span>
|
||||||
</p>
|
</p>
|
||||||
@ -116,8 +116,8 @@
|
|||||||
{% if len(data['servers']) > 0 %}
|
{% if len(data['servers']) > 0 %}
|
||||||
{% if data['user_data']['hints'] %}
|
{% if data['user_data']['hints'] %}
|
||||||
<span class="too_small" title="{{ translate('dashboard', 'cannotSeeOnMobile', data['lang']) }}" ,
|
<span class="too_small" title="{{ translate('dashboard', 'cannotSeeOnMobile', data['lang']) }}" ,
|
||||||
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
|
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
|
||||||
data-placement="top"></span>
|
data-placement="top"></span>
|
||||||
{% end %}
|
{% end %}
|
||||||
{% end %}
|
{% end %}
|
||||||
<div><a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> {{
|
<div><a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> {{
|
||||||
@ -125,19 +125,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
<div class="table-responsive">
|
{% if len(data['servers']) == 0%}
|
||||||
{% if len(data['servers']) == 0%}
|
<div style="text-align: center; color: grey;">
|
||||||
<div style="text-align: center; color: grey;">
|
<h1>{{ translate('dashboard', 'welcome', data['lang']) }}</h1>
|
||||||
<h1>{{ translate('dashboard', 'welcome', data['lang']) }}</h1>
|
<br>
|
||||||
<br>
|
<h7>{{ translate('dashboard', 'no-servers', data['lang']) }} {{ translate('dashboard', 'newServer',
|
||||||
<h7>{{ translate('dashboard', 'no-servers', data['lang']) }} {{ translate('dashboard', 'newServer',
|
data['lang']) }}.</h7>
|
||||||
data['lang']) }}.</h7>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
{% if len(data['servers']) > 0 %}
|
{% if len(data['servers']) > 0 %}
|
||||||
<!-- View for Large screen -->
|
<!-- View for Large screen -->
|
||||||
<table id="servers_table" class="table table-hover d-none d-sm-table">
|
<div class="table-responsive d-none d-sm-block">
|
||||||
|
<table id="servers_table" class="table table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="rounded" id="first" draggable="false">
|
<tr class="rounded" id="first" draggable="false">
|
||||||
<th draggable="false">{{ translate('dashboard', 'server', data['lang']) }}</th>
|
<th draggable="false">{{ translate('dashboard', 'server', data['lang']) }}</th>
|
||||||
@ -163,17 +163,17 @@
|
|||||||
{% if server['user_command_permission'] %}
|
{% if server['user_command_permission'] %}
|
||||||
{% if server['stats']['running'] %}
|
{% if server['stats']['running'] %}
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="stop_button" data-toggle="tooltip"
|
<a data-id="{{server['server_data']['server_id']}}" class="stop_button" data-toggle="tooltip"
|
||||||
title="{{ translate('dashboard', 'stop' , data['lang']) }}">
|
title="{{ translate('dashboard', 'stop' , data['lang']) }}">
|
||||||
<i class="fas fa-stop"></i>
|
<i class="fas fa-stop"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="restart_button" data-toggle="tooltip"
|
<a data-id="{{server['server_data']['server_id']}}" class="restart_button" data-toggle="tooltip"
|
||||||
title="{{ translate('dashboard', 'restart' , data['lang']) }}">
|
title="{{ translate('dashboard', 'restart' , data['lang']) }}">
|
||||||
<i class="fas fa-sync"></i>
|
<i class="fas fa-sync"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="kill_button" data-toggle="tooltip"
|
<a data-id="{{server['server_data']['server_id']}}" class="kill_button" data-toggle="tooltip"
|
||||||
title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
||||||
<i class="fas fa-skull"></i>
|
<i class="fas fa-skull"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@ -192,15 +192,15 @@
|
|||||||
data['lang']) }}</a>
|
data['lang']) }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="play_button" data-toggle="tooltip"
|
<a data-id="{{server['server_data']['server_id']}}" class="play_button" data-toggle="tooltip"
|
||||||
title="{{ translate('dashboard', 'start' , data['lang']) }}">
|
title="{{ translate('dashboard', 'start' , data['lang']) }}">
|
||||||
<i class="fas fa-play"></i>
|
<i class="fas fa-play"></i>
|
||||||
</a>
|
</a>
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="clone_button" data-toggle="tooltip"
|
<a data-id="{{server['server_data']['server_id']}}" class="clone_button" data-toggle="tooltip"
|
||||||
title="{{ translate('dashboard', 'clone' , data['lang']) }}">
|
title="{{ translate('dashboard', 'clone' , data['lang']) }}">
|
||||||
<i class="fas fa-clone"></i>
|
<i class="fas fa-clone"></i>
|
||||||
</a>
|
</a>
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="kill_button" data-toggle="tooltip"
|
<a data-id="{{server['server_data']['server_id']}}" class="kill_button" data-toggle="tooltip"
|
||||||
title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
||||||
<i class="fas fa-skull"></i>
|
<i class="fas fa-skull"></i>
|
||||||
</a>
|
</a>
|
||||||
{% end %}
|
{% end %}
|
||||||
@ -209,7 +209,7 @@
|
|||||||
|
|
||||||
<td draggable="false" id="server_cpu_{{server['server_data']['server_id']}}">
|
<td draggable="false" id="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
|
||||||
@ -219,14 +219,14 @@
|
|||||||
bg-danger
|
bg-danger
|
||||||
{% end %}
|
{% end %}
|
||||||
" role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0"
|
" role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0"
|
||||||
aria-valuemin="0" aria-valuemax="100"></div>
|
aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
</div>
|
</div>
|
||||||
{{server['stats']['cpu']}}%
|
{{server['stats']['cpu']}}%
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td draggable="false" id="server_mem_{{server['server_data']['server_id']}}">
|
<td draggable="false" id="server_mem_{{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']['mem']}}">
|
title="{{server['stats']['mem']}}">
|
||||||
<div class="progress-bar
|
<div class="progress-bar
|
||||||
{% if server['stats']['mem_percent'] <= 33 %}
|
{% if server['stats']['mem_percent'] <= 33 %}
|
||||||
bg-success
|
bg-success
|
||||||
@ -236,7 +236,7 @@
|
|||||||
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>
|
</div>
|
||||||
{{server['stats']['mem_percent']}}% -
|
{{server['stats']['mem_percent']}}% -
|
||||||
|
|
||||||
@ -256,7 +256,7 @@
|
|||||||
|
|
||||||
{% 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 %}
|
||||||
|
|
||||||
@ -280,180 +280,208 @@
|
|||||||
{% end %}
|
{% end %}
|
||||||
</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 %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<!-- View for Small screen -->
|
</div>
|
||||||
<table id="servers_table" class="table table-hover d-table d-sm-none">
|
{% end %}
|
||||||
<thead>
|
{% if len(data['servers']) > 0 %}
|
||||||
<tr class="rounded" id="first" draggable="false">
|
<!-- Try with Accordion -->
|
||||||
<th scope="col" draggable="false">{{ translate('dashboard', 'server', data['lang']) }}</th>
|
<div class="d-sm-none d-block">
|
||||||
<th scope="col" draggable="false">{{ translate('dashboard', 'actions', data['lang']) }}</th>
|
<div class="accordion" id="accordionServers">
|
||||||
<th scope="col" draggable="false">{{ translate('dashboard', 'status', data['lang']) }}</th>
|
{% for server in data['servers'] %}
|
||||||
<th scope="col" draggable="false"></th>
|
<div class="card">
|
||||||
</tr>
|
<div class="card-header" id="heading-{{server['server_data']['server_id']}}">
|
||||||
</thead>
|
<h2 class="mb-0 container overflow-hidden">
|
||||||
<tbody>
|
<div class="row">
|
||||||
{% for server in data['servers'] %}
|
<div class="col-10 col-lg-3 mx-0 px-0">
|
||||||
<tr id="{{server['server_data']['server_id']}}" draggable="false">
|
<a class="btn btn-link d-flex justify-content-start" type="button" href="/panel/server_detail?id={{server['server_data']['server_id']}}">
|
||||||
<td scope="row"><i class="fas fa-server"></i>
|
<i class="fas fa-server"></i> {{ server['server_data']['server_name'] }}
|
||||||
<a draggable="false" href="/panel/server_detail?id={{server['server_data']['server_id']}}">
|
</a>
|
||||||
{{ server['server_data']['server_name'] }}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td draggable="false" id="controls{{server['server_data']['server_id']}}" class="actions_serverlist">
|
|
||||||
{% if server['user_command_permission'] %}
|
|
||||||
{% if server['stats']['running'] %}
|
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="stop_button" data-toggle="tooltip"
|
|
||||||
title="{{ translate('dashboard', 'stop' , data['lang']) }}">
|
|
||||||
<i class="fas fa-stop"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="restart_button" data-toggle="tooltip"
|
|
||||||
title="{{ translate('dashboard', 'restart' , data['lang']) }}">
|
|
||||||
<i class="fas fa-sync"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="kill_button" data-toggle="tooltip"
|
|
||||||
title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
|
||||||
<i class="fas fa-skull"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
{% elif server['stats']['updating']%}
|
|
||||||
<!-- WHAT HAPPENED HERE -->
|
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="">{{ translate('serverTerm', 'updating',
|
|
||||||
data['lang']) }}</i></a>
|
|
||||||
{% elif server['stats']['waiting_start']%}
|
|
||||||
<!-- WHAT HAPPENED HERE -->
|
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="" title="{{
|
|
||||||
translate('dashboard', 'delay-explained' , data['lang'])}}">{{ translate('dashboard', 'starting',
|
|
||||||
data['lang']) }}</i></a>
|
|
||||||
{% elif server['stats']['downloading']%}
|
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class=""><i class="fa fa-spinner fa-spin"></i>
|
|
||||||
{{ translate('serverTerm', 'downloading', data['lang']) }}</a>
|
|
||||||
{% else %}
|
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="play_button" data-toggle="tooltip"
|
|
||||||
title="{{ translate('dashboard', 'start' , data['lang']) }}">
|
|
||||||
<i class="fas fa-play"></i>
|
|
||||||
</a>
|
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="clone_button" data-toggle="tooltip"
|
|
||||||
title="{{ translate('dashboard', 'clone' , data['lang']) }}">
|
|
||||||
<i class="fas fa-clone"></i>
|
|
||||||
</a>
|
|
||||||
<a data-id="{{server['server_data']['server_id']}}" class="kill_button" data-toggle="tooltip"
|
|
||||||
title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
|
||||||
<i class="fas fa-skull"></i>
|
|
||||||
</a>
|
|
||||||
{% end %}
|
|
||||||
{% end %}
|
|
||||||
</td>
|
|
||||||
<td draggable="false" id="m_server_running_status_{{server['server_data']['server_id']}}">
|
|
||||||
{% if server['stats']['running'] %}
|
|
||||||
<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online',
|
|
||||||
data['lang']) }}</span>
|
|
||||||
{% elif server['stats']['crashed'] %}
|
|
||||||
<span class="text-danger"><i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard',
|
|
||||||
'crashed',
|
|
||||||
data['lang']) }}</span>
|
|
||||||
{% else %}
|
|
||||||
<span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline',
|
|
||||||
data['lang']) }}</span>
|
|
||||||
{% end %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span data-toggle="collapse" data-target="#details_{{server['server_data']['server_id']}}"
|
|
||||||
aria-expanded="false" aria-controls="details_{{server['server_data']['server_id']}}"><i
|
|
||||||
class="fas fa-chevron-down"></i></span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr id="details_{{server['server_data']['server_id']}}" class="collapse" draggable="false">
|
|
||||||
<td colspan="4">
|
|
||||||
<div class="collapse" id="details_{{server['server_data']['server_id']}}">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
<h6>{{ translate('dashboard', 'cpuUsage', data['lang']) }}</h6>
|
|
||||||
<div id="m_server_cpu_{{server['server_data']['server_id']}}">
|
|
||||||
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
|
||||||
title="{{server['stats']['cpu']}}">
|
|
||||||
<div class="progress-bar
|
|
||||||
{% if server['stats']['cpu'] <= 33 %}
|
|
||||||
bg-success
|
|
||||||
{% elif 34 <= server['stats']['cpu'] <= 66 %}
|
|
||||||
bg-warning
|
|
||||||
{% else %}
|
|
||||||
bg-danger
|
|
||||||
{% end %}
|
|
||||||
" role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0"
|
|
||||||
aria-valuemin="0" aria-valuemax="100"></div>
|
|
||||||
</div>
|
|
||||||
{{server['stats']['cpu']}}%
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
<h6>{{ translate('dashboard', 'memUsage', data['lang']) }}</h6>
|
|
||||||
<div draggable="false" id="m_server_mem_{{server['server_data']['server_id']}}">
|
|
||||||
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
|
||||||
title="{{server['stats']['mem']}}">
|
|
||||||
<div class="progress-bar
|
|
||||||
{% if server['stats']['mem_percent'] <= 33 %}
|
|
||||||
bg-success
|
|
||||||
{% elif 34 <= server['stats']['mem_percent'] <= 66 %}
|
|
||||||
bg-warning
|
|
||||||
{% else %}
|
|
||||||
bg-danger
|
|
||||||
{% end %}
|
|
||||||
" role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0"
|
|
||||||
aria-valuemin="0" aria-valuemax="100"></div>
|
|
||||||
</div>
|
|
||||||
{{server['stats']['mem_percent']}}% -
|
|
||||||
|
|
||||||
{% if server['stats']['mem'] == 0 %}
|
|
||||||
0 MB
|
|
||||||
{% else %}
|
|
||||||
{{server['stats']['mem']}}
|
|
||||||
{% end %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<div class="col-2 col-lg-3 mx-0 px-0">
|
||||||
<div class="row">
|
<a class="btn btn-link d-flex justify-content-center" type="button" data-toggle="collapse" data-target="#collapse-{{server['server_data']['server_id']}}"
|
||||||
<div class="col-6">
|
aria-expanded="false" aria-controls="collapse-{{server['server_data']['server_id']}}">
|
||||||
<h6>{{ translate('dashboard', 'size', data['lang']) }}</h6>
|
<i class="fas fa-chart-bar"></i>
|
||||||
<div draggable="false" id="m_server_world_{{server['server_data']['server_id']}}">
|
</a>
|
||||||
{{ server['stats']['world_size'] }}
|
</div>
|
||||||
|
<div class="col-4 col-lg-3 mx-0 px-0">
|
||||||
|
<a id="m_server_running_status_{{server['server_data']['server_id']}}" class="btn btn-link d-flex justify-content-start" type="button">
|
||||||
|
{% if server['stats']['running'] %}
|
||||||
|
<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online',
|
||||||
|
data['lang']) }}</span>
|
||||||
|
{% elif server['stats']['crashed'] %}
|
||||||
|
<span class="text-danger"><i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard',
|
||||||
|
'crashed',
|
||||||
|
data['lang']) }}</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline',
|
||||||
|
data['lang']) }}</span>
|
||||||
|
{% end %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-lg-3 mx-0 px-0">
|
||||||
|
<div id="controls{{server['server_data']['server_id']}}" class="container overflow-hidden">
|
||||||
|
{% if server['user_command_permission'] %}
|
||||||
|
{% if server['stats']['running'] %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4 px-0">
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="btn btn-link stop_button actions_serveritem" data-toggle="tooltip"
|
||||||
|
title="{{ translate('dashboard', 'stop' , data['lang']) }}">
|
||||||
|
<i class="fas fa-stop"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-4 px-0">
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="btn btn-link restart_button actions_serveritem" data-toggle="tooltip"
|
||||||
|
title="{{ translate('dashboard', 'restart' , data['lang']) }}">
|
||||||
|
<i class="fas fa-sync"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-4 px-0">
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="btn btn-link kill_button actions_serveritem" data-toggle="tooltip"
|
||||||
|
title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
||||||
|
<i class="fas fa-skull"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% elif server['stats']['updating']%}
|
||||||
<div class="col-6" style="width: auto;">
|
<!-- WHAT HAPPENED HERE -->
|
||||||
<h6>{{ translate('dashboard', 'players', data['lang']) }}</h6>
|
<div class="row">
|
||||||
<div draggable="false" id="m_server_desc_{{server['server_data']['server_id']}}">
|
<div class="col-12 px-0">
|
||||||
{% if server['stats']['int_ping_results'] %}
|
<a data-id="{{server['server_data']['server_id']}}" class="btn btn-link">{{ translate('serverTerm', 'updating',
|
||||||
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard',
|
data['lang']) }}</i></a>
|
||||||
'max',
|
</div>
|
||||||
data['lang']) }} <br />
|
|
||||||
|
|
||||||
{% if server['stats']['desc'] != 'False' %}
|
|
||||||
<div id="desc_id"
|
|
||||||
style="overflow-wrap: break-word !important; max-width: 85px !important; overflow: scroll;">
|
|
||||||
{{ server['stats']['desc'] }}</div> <br />
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
{% if server['stats']['version'] != 'False' %}
|
|
||||||
{{ server['stats']['version'] }}
|
|
||||||
{% end %}
|
|
||||||
{% end %}
|
|
||||||
</div>
|
</div>
|
||||||
|
{% elif server['stats']['waiting_start']%}
|
||||||
|
<!-- WHAT HAPPENED HERE -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 px-0">
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="btn btn-link" title="{{
|
||||||
|
translate('dashboard', 'delay-explained' , data['lang'])}}">{{ translate('dashboard', 'starting',
|
||||||
|
data['lang']) }}</i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% elif server['stats']['downloading']%}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 px-0">
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="btn btn-link"><i class="fa fa-spinner fa-spin"></i>
|
||||||
|
{{ translate('serverTerm', 'downloading', data['lang']) }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4 px-0">
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="btn play_button actions_serveritem" data-toggle="tooltip"
|
||||||
|
title="{{ translate('dashboard', 'start' , data['lang']) }}">
|
||||||
|
<i class="fas fa-play"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-4 px-0">
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="btn clone_button actions_serveritem" data-toggle="tooltip"
|
||||||
|
title="{{ translate('dashboard', 'clone' , data['lang']) }}">
|
||||||
|
<i class="fas fa-clone"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-4 px-0">
|
||||||
|
<a data-id="{{server['server_data']['server_id']}}" class="btn kill_button actions_serveritem" data-toggle="tooltip"
|
||||||
|
title="{{ translate('dashboard', 'kill' , data['lang']) }}">
|
||||||
|
<i class="fas fa-skull"></i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% end %}
|
||||||
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</h2>
|
||||||
</tr>
|
</div>
|
||||||
{% end %}
|
|
||||||
</tbody>
|
<div id="collapse-{{server['server_data']['server_id']}}" class="collapse" aria-labelledby="heading-{{server['server_data']['server_id']}}" data-parent="#accordionServers">
|
||||||
</table>
|
<div class="card-body">
|
||||||
{% end %}
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<h6>{{ translate('dashboard', 'cpuUsage', data['lang']) }}</h6>
|
||||||
|
<div id="m_server_cpu_{{server['server_data']['server_id']}}">
|
||||||
|
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
||||||
|
title="{{server['stats']['cpu']}}">
|
||||||
|
<div class="progress-bar
|
||||||
|
{% if server['stats']['cpu'] <= 33 %}
|
||||||
|
bg-success
|
||||||
|
{% elif 34 <= server['stats']['cpu'] <= 66 %}
|
||||||
|
bg-warning
|
||||||
|
{% else %}
|
||||||
|
bg-danger
|
||||||
|
{% end %}
|
||||||
|
" role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0"
|
||||||
|
aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
|
</div>
|
||||||
|
{{server['stats']['cpu']}}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<h6>{{ translate('dashboard', 'memUsage', data['lang']) }}</h6>
|
||||||
|
<div draggable="false" id="m_server_mem_{{server['server_data']['server_id']}}">
|
||||||
|
<div class="progress mb-1" data-toggle="tooltip" data-placement="top"
|
||||||
|
title="{{server['stats']['mem']}}">
|
||||||
|
<div class="progress-bar
|
||||||
|
{% if server['stats']['mem_percent'] <= 33 %}
|
||||||
|
bg-success
|
||||||
|
{% elif 34 <= server['stats']['mem_percent'] <= 66 %}
|
||||||
|
bg-warning
|
||||||
|
{% else %}
|
||||||
|
bg-danger
|
||||||
|
{% end %}
|
||||||
|
" role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0"
|
||||||
|
aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
|
</div>
|
||||||
|
{{server['stats']['mem_percent']}}% -
|
||||||
|
|
||||||
|
{% if server['stats']['mem'] == 0 %}
|
||||||
|
0 MB
|
||||||
|
{% else %}
|
||||||
|
{{server['stats']['mem']}}
|
||||||
|
{% end %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<h6>{{ translate('dashboard', 'size', data['lang']) }}</h6>
|
||||||
|
<div draggable="false" id="m_server_world_{{server['server_data']['server_id']}}">
|
||||||
|
{{ server['stats']['world_size'] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6" style="width: auto;">
|
||||||
|
<h6>{{ translate('dashboard', 'players', data['lang']) }}</h6>
|
||||||
|
<div draggable="false" id="m_server_desc_{{server['server_data']['server_id']}}">
|
||||||
|
{% if server['stats']['int_ping_results'] %}
|
||||||
|
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard',
|
||||||
|
'max',
|
||||||
|
data['lang']) }} <br />
|
||||||
|
|
||||||
|
{% if server['stats']['desc'] != 'False' %}
|
||||||
|
<div id="desc_id"
|
||||||
|
style="overflow-wrap: break-word !important; max-width: 85px !important; overflow: scroll;">
|
||||||
|
{{ server['stats']['desc'] }}</div> <br />
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% if server['stats']['version'] != 'False' %}
|
||||||
|
{{ server['stats']['version'] }}
|
||||||
|
{% end %}
|
||||||
|
{% end %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% end %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -909,4 +937,4 @@
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
@ -4,7 +4,7 @@
|
|||||||
<!-- Required meta tags -->
|
<!-- Required meta tags -->
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<title>Crafty Commander</title>
|
<title>Crafty Controller</title>
|
||||||
<!-- plugins:css -->
|
<!-- plugins:css -->
|
||||||
<link rel="stylesheet" href="/static/assets/vendors/mdi/css/materialdesignicons.min.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/flag-icon-css/css/flag-icon.min.css">
|
||||||
|
@ -19,11 +19,18 @@
|
|||||||
</li>
|
</li>
|
||||||
{% end %}
|
{% end %}
|
||||||
{% if data['permissions']['Backup'] in data['user_permissions'] %}
|
{% if data['permissions']['Backup'] in data['user_permissions'] %}
|
||||||
|
{% if data['backup_failed'] %}
|
||||||
|
<li class="nav-item term-nav-item">
|
||||||
|
<a style="color: red !important;" class="nav-link {% if data['active_link'] == 'backup' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
|
||||||
|
<i class="fas fa-save"></i>{{ translate('serverDetails', 'backup', data['lang']) }} <i class="fas fa-exclamation-triangle"> </i></a>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
<li class="nav-item term-nav-item">
|
<li class="nav-item term-nav-item">
|
||||||
<a class="nav-link {% if data['active_link'] == 'backup' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
|
<a class="nav-link {% if data['active_link'] == 'backup' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
|
||||||
<i class="fas fa-save"></i>{{ translate('serverDetails', 'backup', data['lang']) }}</a>
|
<i class="fas fa-save"></i>{{ translate('serverDetails', 'backup', data['lang']) }}</a>
|
||||||
</li>
|
</li>
|
||||||
{% end %}
|
{% end %}
|
||||||
|
{% end %}
|
||||||
{% if data['permissions']['Files'] in data['user_permissions'] %}
|
{% if data['permissions']['Files'] in data['user_permissions'] %}
|
||||||
<li class="nav-item term-nav-item">
|
<li class="nav-item term-nav-item">
|
||||||
<a class="nav-link {% if data['active_link'] == 'files' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
|
<a class="nav-link {% if data['active_link'] == 'files' %}active{% end %}" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
|
||||||
|
@ -14,7 +14,8 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h4 class="page-title">
|
<h4 class="page-title">
|
||||||
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
|
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
|
||||||
|
data['server_stats']['server_id']['server_name'] }}
|
||||||
<br />
|
<br />
|
||||||
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
|
||||||
</h4>
|
</h4>
|
||||||
@ -51,55 +52,85 @@
|
|||||||
|
|
||||||
{% if data['backing_up'] %}
|
{% if data['backing_up'] %}
|
||||||
<div class="progress" style="height: 15px;">
|
<div class="progress" style="height: 15px;">
|
||||||
<div class="progress-bar progress-bar-striped progress-bar-animated" id="backup_progress_bar" role="progressbar" style="width:{{data['backup_stats']['percent']}}%;" aria-valuenow="{{data['backup_stats']['percent']}}" aria-valuemin="0" aria-valuemax="100">{{ data['backup_stats']['percent'] }}%</div>
|
<div class="progress-bar progress-bar-striped progress-bar-animated" id="backup_progress_bar"
|
||||||
|
role="progressbar" style="width:{{data['backup_stats']['percent']}}%;"
|
||||||
|
aria-valuenow="{{data['backup_stats']['percent']}}" aria-valuemin="0" aria-valuemax="100">{{
|
||||||
|
data['backup_stats']['percent'] }}%</div>
|
||||||
</div>
|
</div>
|
||||||
<p>Backing up <span id="total_files">{{data['backup_stats']['total_files']}}</span> Files</p>
|
<p>Backing up <i class="fas fa-spin fa-spinner"></i> <span
|
||||||
|
id="total_files">{{data['server_stats']['world_size']}}</span></p>
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
{% if not data['backing_up'] %}
|
{% if not data['backing_up'] %}
|
||||||
<div id="backup_button" class="form-group">
|
<div id="backup_button" class="form-group">
|
||||||
<button class="btn btn-primary" id="backup_now_button">{{ translate('serverBackups', 'backupNow', data['lang']) }}</button>
|
<button class="btn btn-primary" id="backup_now_button">{{ translate('serverBackups', 'backupNow',
|
||||||
|
data['lang']) }}</button>
|
||||||
</div>
|
</div>
|
||||||
{% end %}
|
{% end %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
{% if data['super_user'] %}
|
{% if data['super_user'] %}
|
||||||
<label for="server_name">{{ translate('serverBackups', 'storageLocation', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverBackups', 'storageLocationDesc', data['lang']) }}</small> </label>
|
<label for="server_name">{{ translate('serverBackups', 'storageLocation', data['lang']) }} <small
|
||||||
<input type="text" class="form-control" name="backup_path" id="backup_path" value="{{ data['server_stats']['server_id']['backup_path'] }}" placeholder="{{ translate('serverBackups', 'storageLocation', data['lang']) }}">
|
class="text-muted ml-1"> - {{ translate('serverBackups', 'storageLocationDesc', data['lang'])
|
||||||
|
}}</small> </label>
|
||||||
|
<input type="text" class="form-control" name="backup_path" id="backup_path"
|
||||||
|
value="{{ data['server_stats']['server_id']['backup_path'] }}"
|
||||||
|
placeholder="{{ translate('serverBackups', 'storageLocation', data['lang']) }}">
|
||||||
{% end %}
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="server_path">{{ translate('serverBackups', 'maxBackups', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('serverBackups', 'maxBackupsDesc', data['lang']) }}</small> </label>
|
<label for="server_path">{{ translate('serverBackups', 'maxBackups', data['lang']) }} <small
|
||||||
<input type="text" class="form-control" name="max_backups" id="max_backups" value="{{ data['backup_config']['max_backups'] }}" placeholder="{{ translate('serverBackups', 'maxBackups', data['lang']) }}">
|
class="text-muted ml-1"> - {{ translate('serverBackups', 'maxBackupsDesc', data['lang'])
|
||||||
|
}}</small> </label>
|
||||||
|
<input type="text" class="form-control" name="max_backups" id="max_backups"
|
||||||
|
value="{{ data['backup_config']['max_backups'] }}"
|
||||||
|
placeholder="{{ translate('serverBackups', 'maxBackups', data['lang']) }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="compress" class="form-check-label ml-4 mb-4"></label>
|
<label for="compress" class="form-check-label ml-4 mb-4"></label>
|
||||||
{% if data['backup_config']['compress'] %}
|
{% if data['backup_config']['compress'] %}
|
||||||
<input type="checkbox" class="form-check-input" id="compress" name="compress"
|
<input type="checkbox" class="form-check-input" id="compress" name="compress" checked=""
|
||||||
checked="" value="True">{{ translate('serverBackups', 'compress', data['lang']) }}
|
value="True">{{ translate('serverBackups', 'compress', data['lang']) }}
|
||||||
{% else %}
|
{% else %}
|
||||||
<input type="checkbox" class="form-check-input" id="compress" name="compress"
|
<input type="checkbox" class="form-check-input" id="compress" name="compress" value="True">{{
|
||||||
value="True">{{ translate('serverBackups', 'compress', data['lang']) }}
|
translate('serverBackups', 'compress', data['lang']) }}
|
||||||
{% end %}
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="server">{{ translate('serverBackups', 'exclusionsTitle', data['lang']) }} <small> - {{ translate('serverBackups', 'excludedChoose', data['lang']) }}</small></label>
|
<label for="shutdown" class="form-check-label ml-4 mb-4"></label>
|
||||||
<br>
|
{% if data['backup_config']['shutdown'] %}
|
||||||
<button class="btn btn-primary mr-2" id="root_files_button" data-server_path="{{ data['server_stats']['server_id']['path']}}" type="button">{{ translate('serverBackups', 'clickExclude', data['lang']) }}</button>
|
<input type="checkbox" class="form-check-input" id="shutdown" name="shutdown" checked=""
|
||||||
|
value="True">{{ translate('serverBackups', 'shutdown', data['lang']) }}
|
||||||
|
{% else %}
|
||||||
|
<input type="checkbox" class="form-check-input" id="shutdown" name="shutdown" value="True">{{
|
||||||
|
translate('serverBackups', 'shutdown', data['lang']) }}
|
||||||
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
<input type="number" class="form-control" name="changed" id="changed" value="0" style="visibility: hidden;"></input>
|
<div class="form-group">
|
||||||
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select" aria-hidden="true">
|
<label for="server">{{ translate('serverBackups', 'exclusionsTitle', data['lang']) }} <small> - {{
|
||||||
|
translate('serverBackups', 'excludedChoose', data['lang']) }}</small></label>
|
||||||
|
<br>
|
||||||
|
<button class="btn btn-primary mr-2" id="root_files_button"
|
||||||
|
data-server_path="{{ data['server_stats']['server_id']['path']}}" type="button">{{
|
||||||
|
translate('serverBackups', 'clickExclude', data['lang']) }}</button>
|
||||||
|
</div>
|
||||||
|
<input type="number" class="form-control" name="changed" id="changed" value="0"
|
||||||
|
style="visibility: hidden;"></input>
|
||||||
|
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select"
|
||||||
|
aria-hidden="true">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="exampleModalLongTitle">{{ translate('serverBackups', 'excludedChoose', data['lang']) }}</h5>
|
<h5 class="modal-title" id="exampleModalLongTitle">{{ translate('serverBackups',
|
||||||
|
'excludedChoose', data['lang']) }}</h5>
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="tree-ctx-item" id="main-tree-div" data-path="" style="overflow: scroll; max-height:75%;">
|
<div class="tree-ctx-item" id="main-tree-div" data-path=""
|
||||||
|
style="overflow: scroll; max-height:75%;">
|
||||||
<input type="checkbox" id="main-tree-input" name="root_path" value="" disabled>
|
<input type="checkbox" id="main-tree-input" name="root_path" value="" disabled>
|
||||||
<span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path="">
|
<span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path="">
|
||||||
<i class="far fa-folder"></i>
|
<i class="far fa-folder"></i>
|
||||||
@ -110,15 +141,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" id="modal-cancel" class="btn btn-secondary" data-dismiss="modal">{{ translate('serverBackups', 'cancel', data['lang']) }}</button>
|
<button type="button" id="modal-cancel" class="btn btn-secondary" data-dismiss="modal">{{
|
||||||
<button type="button" id="modal-okay" data-dismiss="modal" class="btn btn-primary">{{ translate('serverWizard', 'save', data['lang']) }}</button>
|
translate('serverBackups', 'cancel', data['lang']) }}</button>
|
||||||
|
<button type="button" id="modal-okay" data-dismiss="modal" class="btn btn-primary">{{
|
||||||
|
translate('serverWizard', 'save', data['lang']) }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-success mr-2">{{ translate('serverBackups', 'save', data['lang']) }}</button>
|
<button type="submit" class="btn btn-success mr-2">{{ translate('serverBackups', 'save', data['lang'])
|
||||||
<button type="reset" class="btn btn-light">{{ translate('serverBackups', 'cancel', data['lang']) }}</button>
|
}}</button>
|
||||||
|
<button type="reset" class="btn btn-light">{{ translate('serverBackups', 'cancel', data['lang'])
|
||||||
|
}}</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -138,13 +173,15 @@
|
|||||||
{% for backup in data['backup_list'] %}
|
{% for backup in data['backup_list'] %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="/panel/download_backup?file={{ backup['path'] }}&id={{ data['server_stats']['server_id']['server_id'] }}" class="btn btn-primary">
|
<a href="/panel/download_backup?file={{ backup['path'] }}&id={{ data['server_stats']['server_id']['server_id'] }}"
|
||||||
|
class="btn btn-primary">
|
||||||
<i class="fas fa-download" aria-hidden="true"></i>
|
<i class="fas fa-download" aria-hidden="true"></i>
|
||||||
{{ translate('serverBackups', 'download', data['lang']) }}
|
{{ translate('serverBackups', 'download', data['lang']) }}
|
||||||
</a>
|
</a>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<button data-file="{{ backup['path'] }}" data-backup_path="{{ data['backup_path'] }}" class="btn btn-danger del_button">
|
<button data-file="{{ backup['path'] }}" data-backup_path="{{ data['backup_path'] }}"
|
||||||
|
class="btn btn-danger del_button">
|
||||||
<i class="fas fa-trash" aria-hidden="true"></i>
|
<i class="fas fa-trash" aria-hidden="true"></i>
|
||||||
{{ translate('serverBackups', 'delete', data['lang']) }}
|
{{ translate('serverBackups', 'delete', data['lang']) }}
|
||||||
</button>
|
</button>
|
||||||
@ -168,7 +205,8 @@
|
|||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
<h4 class="card-title"><i class="fas fa-server"></i> {{ translate('serverBackups', 'excludedBackups', data['lang']) }} <small class="text-muted ml-1"></small> </h4>
|
<h4 class="card-title"><i class="fas fa-server"></i> {{ translate('serverBackups', 'excludedBackups',
|
||||||
|
data['lang']) }} <small class="text-muted ml-1"></small> </h4>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-sm-12">
|
<div class="col-md-6 col-sm-12">
|
||||||
<form class="forms-sample" method="post" action="/panel/server_detail">
|
<form class="forms-sample" method="post" id="config_form" action="/panel/server_detail">
|
||||||
{% raw xsrf_form_html() %}
|
{% raw xsrf_form_html() %}
|
||||||
<input type="hidden" name="id" value="{{ data['server_stats']['server_id']['server_id'] }}">
|
<input type="hidden" name="id" value="{{ data['server_stats']['server_id']['server_id'] }}">
|
||||||
<input type="hidden" name="subpage" value="config">
|
<input type="hidden" name="subpage" value="config">
|
||||||
@ -50,8 +50,8 @@
|
|||||||
placeholder="{{ translate('serverConfig', 'serverName', data['lang']) }}" required>
|
placeholder="{{ translate('serverConfig', 'serverName', data['lang']) }}" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if data['super_user'] %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
{% if data['super_user'] %}
|
|
||||||
<label for="server_path">{{ translate('serverConfig', 'serverPath', data['lang']) }} <small
|
<label for="server_path">{{ translate('serverConfig', 'serverPath', data['lang']) }} <small
|
||||||
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverPathDesc', data['lang']) }}</small>
|
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverPathDesc', data['lang']) }}</small>
|
||||||
</label>
|
</label>
|
||||||
@ -78,7 +78,24 @@
|
|||||||
value="{{ data['server_stats']['server_id']['executable'] }}"
|
value="{{ data['server_stats']['server_id']['executable'] }}"
|
||||||
placeholder="{{ translate('serverConfig', 'serverExecutable', data['lang']) }}" required>
|
placeholder="{{ translate('serverConfig', 'serverExecutable', data['lang']) }}" required>
|
||||||
</div>
|
</div>
|
||||||
|
{% end %}
|
||||||
|
{% if data['server_stats']['server_type'] == "minecraft-java" %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="java_selection">{{ translate('serverConfig', 'javaVersion', data['lang']) }}
|
||||||
|
<small class="text-muted ml-1">{{ translate('serverConfig', 'javaVersionDesc', data['lang']) }}</small>
|
||||||
|
</label>
|
||||||
|
<select class="form-select form-control form-control-lg select-css" id="java_selection"
|
||||||
|
name="java_selection" form="config_form">
|
||||||
|
<option value="">{{ translate('serverConfig',
|
||||||
|
'javaNoChange', data['lang'])}}</option>
|
||||||
|
{% for path in data['java_versions'] %}
|
||||||
|
<option value="{{path}}">{{path}}</option>
|
||||||
|
{% end %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% if data['super_user'] %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="execution_command">{{ translate('serverConfig', 'serverExecutionCommand', data['lang']) }}
|
<label for="execution_command">{{ translate('serverConfig', 'serverExecutionCommand', data['lang']) }}
|
||||||
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverExecutionCommandDesc',
|
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverExecutionCommandDesc',
|
||||||
@ -86,8 +103,14 @@
|
|||||||
<input type="text" class="form-control" name="execution_command" id="execution_command"
|
<input type="text" class="form-control" name="execution_command" id="execution_command"
|
||||||
value="{{ data['server_stats']['server_id']['execution_command'] }}"
|
value="{{ data['server_stats']['server_id']['execution_command'] }}"
|
||||||
placeholder="{{ translate('serverConfig', 'serverExecutionCommand', data['lang']) }}" required>
|
placeholder="{{ translate('serverConfig', 'serverExecutionCommand', data['lang']) }}" required>
|
||||||
{% end %}
|
|
||||||
</div>
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<label for="execution_command">{{ translate('serverConfig', 'serverExecutionCommand', data['lang']) }}
|
||||||
|
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||||
|
<span style="color: gray;">{{ data['server_stats']['server_id']['execution_command'] }}</span> 🔒
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
{% end %}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="stop_command">{{ translate('serverConfig', 'serverStopCommand', data['lang']) }} <small
|
<label for="stop_command">{{ translate('serverConfig', 'serverStopCommand', data['lang']) }} <small
|
||||||
|
50
app/frontend/templates/panel/wiki.html
Normal file
50
app/frontend/templates/panel/wiki.html
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
{% extends ../base.html %}
|
||||||
|
|
||||||
|
{% block meta %}
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% block title %}Crafty Controller - Wiki{% 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">Wiki</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 grid-margin">
|
||||||
|
<iframe src="https://wiki.craftycontrol.com" width=100% height=2200px title="crafty's wiki"></iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- content-wrapper ends -->
|
||||||
|
<style>
|
||||||
|
.popover-body {
|
||||||
|
color: white !important;
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
#desc_id {
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
/* for Internet Explorer, Edge */
|
||||||
|
scrollbar-width: none;
|
||||||
|
/* for Firefox */
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
#desc_id::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
/* for Chrome, Safari, and Opera */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
{% end %}
|
@ -4,7 +4,7 @@
|
|||||||
<!-- Required meta tags -->
|
<!-- Required meta tags -->
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<title>Crafty Commander</title>
|
<title>Crafty Controller</title>
|
||||||
<!-- plugins:css -->
|
<!-- plugins:css -->
|
||||||
<link rel="stylesheet" href="/static/assets/vendors/mdi/css/materialdesignicons.min.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/flag-icon-css/css/flag-icon.min.css">
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<!-- Required meta tags -->
|
<!-- Required meta tags -->
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<title>Crafty Commander</title>
|
<title>Crafty Controller</title>
|
||||||
<!-- plugins:css -->
|
<!-- plugins:css -->
|
||||||
<link rel="stylesheet" href="/static/assets/vendors/mdi/css/materialdesignicons.min.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/flag-icon-css/css/flag-icon.min.css">
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<!-- Required meta tags -->
|
<!-- Required meta tags -->
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<title>Crafty Commander</title>
|
<title>Crafty Controller</title>
|
||||||
<!-- plugins:css -->
|
<!-- plugins:css -->
|
||||||
<link rel="stylesheet" href="/static/assets/vendors/mdi/css/materialdesignicons.min.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/flag-icon-css/css/flag-icon.min.css">
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="server_type">{{ translate('serverWizard', 'serverType', data['lang']) }}</label>
|
<label for="server_type">{{ translate('serverWizard', 'serverType', data['lang']) }}</label>
|
||||||
<select required class="form-control form-control-lg select-css" id="server_type" name="server_type"
|
<select required class="form-control form-control-lg select-css" id="server_type" name="server_type"
|
||||||
onchange="serverTypeChange(this)">
|
onchange="serverTypeChange(this)">
|
||||||
<option value="">{{ translate('serverWizard', 'selectType', data['lang']) }}</option>
|
<option value="">{{ translate('serverWizard', 'selectType', data['lang']) }}</option>
|
||||||
{% for s in data['server_types'] %}
|
{% for s in data['server_types'] %}
|
||||||
<option value="{{ s }}">{{ s.capitalize() }}</option>
|
<option value="{{ s }}">{{ s.capitalize() }}</option>
|
||||||
@ -56,42 +56,42 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label>
|
<label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label>
|
||||||
<input type="text" class="form-control" id="server_name" name="server_name"
|
<input type="text" class="form-control" id="server_name" name="server_name"
|
||||||
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
|
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small
|
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small
|
||||||
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
|
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
|
||||||
data['lang']) }}</small></h4>
|
data['lang']) }}</small></h4>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-sm-3">
|
<div class="col-sm-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="min_memory1">{{ translate('serverWizard', 'minMem', data['lang']) }} <small> - {{
|
<label for="min_memory1">{{ translate('serverWizard', 'minMem', data['lang']) }} <small> - {{
|
||||||
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
||||||
<input type="number" class="form-control" id="min_memory1" name="min_memory" value="1" step="0.5"
|
<input type="number" class="form-control" id="min_memory1" name="min_memory" value="1" step="0.5"
|
||||||
min="0.5" required>
|
min="0.5" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-3 offset-1">
|
<div class="col-sm-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="max_memory1">{{ translate('serverWizard', 'maxMem', data['lang']) }} <small> - {{
|
<label for="max_memory1">{{ translate('serverWizard', 'maxMem', data['lang']) }} <small> - {{
|
||||||
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
||||||
<input type="number" class="form-control" id="max_memory1" name="max_memory" value="2" step="0.5"
|
<input type="number" class="form-control" id="max_memory1" name="max_memory" value="2" step="0.5"
|
||||||
min="0.5" required>
|
min="0.5" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-3 offset-1">
|
<div class="col-sm-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="port1">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small> - {{
|
<label for="port1">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small> - {{
|
||||||
translate('serverWizard', 'defaultPort', data['lang']) }}</small></label>
|
translate('serverWizard', 'defaultPort', data['lang']) }}</small></label>
|
||||||
<input type="number" class="form-control" id="port1" name="port" value="25565" step="1" min="1"
|
<input type="number" class="form-control" id="port1" name="port" value="25565" step="1" min="1"
|
||||||
required>
|
required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
@ -100,7 +100,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header p-2" id="Role-1">
|
<div class="card-header p-2" id="Role-1">
|
||||||
<p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-1" aria-expanded="true"
|
<p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-1" aria-expanded="true"
|
||||||
aria-controls="collapseRole-1">
|
aria-controls="collapseRole-1">
|
||||||
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang']) }}
|
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang']) }}
|
||||||
<small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate',
|
<small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate',
|
||||||
data['lang']) }}</small>
|
data['lang']) }}</small>
|
||||||
@ -111,7 +111,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
{% for r in data['roles'] %}
|
{% for r in data['roles'] %}
|
||||||
<span class="d-block menu-option"><label><input name="{{ r['role_id'] }}"
|
<span class="d-block menu-option"><label><input name="{{ r['role_id'] }}"
|
||||||
type="checkbox">
|
type="checkbox">
|
||||||
{{ r['role_name'].capitalize() }}</label></span>
|
{{ r['role_name'].capitalize() }}</label></span>
|
||||||
{% end %}
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
@ -152,7 +152,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label>
|
<label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label>
|
||||||
<input type="text" class="form-control" id="server_name" name="server_name" value=""
|
<input type="text" class="form-control" id="server_name" name="server_name" value=""
|
||||||
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
|
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -161,7 +161,7 @@
|
|||||||
<label for="server">{{ translate('serverWizard', 'serverPath', data['lang']) }} <small>{{
|
<label for="server">{{ translate('serverWizard', 'serverPath', data['lang']) }} <small>{{
|
||||||
translate('serverWizard', 'absoluteServerPath', data['lang']) }}</small></label>
|
translate('serverWizard', 'absoluteServerPath', data['lang']) }}</small></label>
|
||||||
<input type="text" class="form-control" id="server_path" name="server_path"
|
<input type="text" class="form-control" id="server_path" name="server_path"
|
||||||
placeholder="/var/opt/server" required>
|
placeholder="/var/opt/server" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -169,7 +169,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="server_jar">{{ translate('serverWizard', 'serverJar', data['lang']) }}</label>
|
<label for="server_jar">{{ translate('serverWizard', 'serverJar', data['lang']) }}</label>
|
||||||
<input type="text" class="form-control" id="server_jar" name="server_jar" value=""
|
<input type="text" class="form-control" id="server_jar" name="server_jar" value=""
|
||||||
placeholder="paper.jar" required>
|
placeholder="paper.jar" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -178,35 +178,35 @@
|
|||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small
|
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small
|
||||||
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
|
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
|
||||||
data['lang']) }}</small></h4>
|
data['lang']) }}</small></h4>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-sm-3">
|
<div class="col-sm-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="min_memory2">{{ translate('serverWizard', 'minMem', data['lang']) }} <small> - {{
|
<label for="min_memory2">{{ translate('serverWizard', 'minMem', data['lang']) }} <small> - {{
|
||||||
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
||||||
<input type="number" class="form-control" id="min_memory2" name="min_memory" value="1" step="0.5"
|
<input type="number" class="form-control" id="min_memory2" name="min_memory" value="1" step="0.5"
|
||||||
min="0.5" required>
|
min="0.5" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-3 offset-1">
|
<div class="col-sm-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="max_memory2">{{ translate('serverWizard', 'maxMem', data['lang']) }} <small> - {{
|
<label for="max_memory2">{{ translate('serverWizard', 'maxMem', data['lang']) }} <small> - {{
|
||||||
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
||||||
<input type="number" class="form-control" id="max_memory2" name="max_memory" value="2" step="0.5"
|
<input type="number" class="form-control" id="max_memory2" name="max_memory" value="2" step="0.5"
|
||||||
min="0.5" required>
|
min="0.5" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-3 offset-1">
|
<div class="col-sm-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="port2">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small> - {{
|
<label for="port2">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small> - {{
|
||||||
translate('serverWizard', 'defaultPort', data['lang']) }}</small></label>
|
translate('serverWizard', 'defaultPort', data['lang']) }}</small></label>
|
||||||
<input type="number" class="form-control" id="port2" name="port" value="25565" step="1" min="1"
|
<input type="number" class="form-control" id="port2" name="port" value="25565" step="1" min="1"
|
||||||
required>
|
required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
@ -215,7 +215,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header p-2" id="Role-2">
|
<div class="card-header p-2" id="Role-2">
|
||||||
<p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-2" aria-expanded="true"
|
<p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-2" aria-expanded="true"
|
||||||
aria-controls="collapseRole-2">
|
aria-controls="collapseRole-2">
|
||||||
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang']) }}
|
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang']) }}
|
||||||
<small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate',
|
<small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate',
|
||||||
data['lang']) }}</small>
|
data['lang']) }}</small>
|
||||||
@ -226,7 +226,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
{% for r in data['roles'] %}
|
{% for r in data['roles'] %}
|
||||||
<span class="d-block menu-option"><label><input name="{{ r['role_id'] }}"
|
<span class="d-block menu-option"><label><input name="{{ r['role_id'] }}"
|
||||||
type="checkbox">
|
type="checkbox">
|
||||||
{{ r['role_name'].capitalize() }}</label></span>
|
{{ r['role_name'].capitalize() }}</label></span>
|
||||||
{% end %}
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
@ -266,7 +266,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label>
|
<label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label>
|
||||||
<input type="text" class="form-control" id="server_name" name="server_name" value=""
|
<input type="text" class="form-control" id="server_name" name="server_name" value=""
|
||||||
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
|
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -275,7 +275,7 @@
|
|||||||
<label for="server">{{ translate('serverWizard', 'zipPath', data['lang']) }} <small>{{
|
<label for="server">{{ translate('serverWizard', 'zipPath', data['lang']) }} <small>{{
|
||||||
translate('serverWizard', 'absoluteZipPath', data['lang']) }}</small></label>
|
translate('serverWizard', 'absoluteZipPath', data['lang']) }}</small></label>
|
||||||
<input type="text" class="form-control" id="server_path" name="server_path"
|
<input type="text" class="form-control" id="server_path" name="server_path"
|
||||||
placeholder="/var/opt/server.zip" required>
|
placeholder="/var/opt/server.zip" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -294,7 +294,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="server_jar">{{ translate('serverWizard', 'serverJar', data['lang']) }}</label>
|
<label for="server_jar">{{ translate('serverWizard', 'serverJar', data['lang']) }}</label>
|
||||||
<input type="text" class="form-control" id="server_jar" name="server_jar" value=""
|
<input type="text" class="form-control" id="server_jar" name="server_jar" value=""
|
||||||
placeholder="paper.jar" required>
|
placeholder="paper.jar" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -303,7 +303,7 @@
|
|||||||
|
|
||||||
<div class="col-sm-3">
|
<div class="col-sm-3">
|
||||||
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small
|
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small
|
||||||
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
|
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
|
||||||
data['lang']) }}</small></h4>
|
data['lang']) }}</small></h4>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -313,7 +313,7 @@
|
|||||||
<label for="min_memory3">{{ translate('serverWizard', 'minMem', data['lang']) }} <small> - {{
|
<label for="min_memory3">{{ translate('serverWizard', 'minMem', data['lang']) }} <small> - {{
|
||||||
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
||||||
<input type="number" class="form-control" id="min_memory3" name="min_memory" value="1" step="0.5"
|
<input type="number" class="form-control" id="min_memory3" name="min_memory" value="1" step="0.5"
|
||||||
min="0.5" required>
|
min="0.5" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -322,7 +322,7 @@
|
|||||||
<label for="max_memory3">{{ translate('serverWizard', 'maxMem', data['lang']) }} <small> - {{
|
<label for="max_memory3">{{ translate('serverWizard', 'maxMem', data['lang']) }} <small> - {{
|
||||||
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
|
||||||
<input type="number" class="form-control" id="max_memory3" name="max_memory" value="2" step="0.5"
|
<input type="number" class="form-control" id="max_memory3" name="max_memory" value="2" step="0.5"
|
||||||
min="0.5" required>
|
min="0.5" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -331,7 +331,7 @@
|
|||||||
<label for="port3">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small> - {{
|
<label for="port3">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small> - {{
|
||||||
translate('serverWizard', 'defaultPort', data['lang']) }}</small></label>
|
translate('serverWizard', 'defaultPort', data['lang']) }}</small></label>
|
||||||
<input type="number" class="form-control" id="port3" name="port" value="25565" step="1" min="1"
|
<input type="number" class="form-control" id="port3" name="port" value="25565" step="1" min="1"
|
||||||
required>
|
required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -341,7 +341,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header p-2" id="Role-3">
|
<div class="card-header p-2" id="Role-3">
|
||||||
<p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-3"
|
<p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-3"
|
||||||
aria-expanded="true" aria-controls="collapseRole-3">
|
aria-expanded="true" aria-controls="collapseRole-3">
|
||||||
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang'])
|
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang'])
|
||||||
}} <small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate',
|
}} <small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate',
|
||||||
data['lang']) }}</small>
|
data['lang']) }}</small>
|
||||||
@ -352,7 +352,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
{% for r in data['roles'] %}
|
{% for r in data['roles'] %}
|
||||||
<span class="d-block menu-option"><label><input name="{{ r['role_id'] }}"
|
<span class="d-block menu-option"><label><input name="{{ r['role_id'] }}"
|
||||||
type="checkbox">
|
type="checkbox">
|
||||||
{{ r['role_name'].capitalize() }}</label></span>
|
{{ r['role_name'].capitalize() }}</label></span>
|
||||||
{% end %}
|
{% end %}
|
||||||
</div>
|
</div>
|
||||||
@ -368,7 +368,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select"
|
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select"
|
||||||
aria-hidden="true">
|
aria-hidden="true">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
@ -380,7 +380,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="tree-ctx-item" id="main-tree-div" data-path=""
|
<div class="tree-ctx-item" id="main-tree-div" data-path=""
|
||||||
style="overflow: scroll; max-height:75%;">
|
style="overflow: scroll; max-height:75%;">
|
||||||
<input type="radio" id="main-tree-input" name="root_path" value="" checked>
|
<input type="radio" id="main-tree-input" name="root_path" value="" checked>
|
||||||
<span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path="">
|
<span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path="">
|
||||||
<i class="far fa-folder"></i>
|
<i class="far fa-folder"></i>
|
||||||
@ -401,7 +401,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button id="zip_submit" type="submit" title="You must select server root dir first" disabled
|
<button id="zip_submit" type="submit" title="You must select server root dir first" disabled
|
||||||
class="btn btn-primary mr-2">{{ translate('serverWizard', 'importServerButton', data['lang'])
|
class="btn btn-primary mr-2">{{ translate('serverWizard', 'importServerButton', data['lang'])
|
||||||
}}</button>
|
}}</button>
|
||||||
<button type="reset" class="btn btn-danger mr-2">{{ translate('serverWizard', 'resetForm', data['lang'])
|
<button type="reset" class="btn btn-danger mr-2">{{ translate('serverWizard', 'resetForm', data['lang'])
|
||||||
}}</button>
|
}}</button>
|
||||||
@ -710,4 +710,4 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
{% end %}
|
{% end %}
|
16
app/migrations/20220618_crafty_api_secret.py
Normal file
16
app/migrations/20220618_crafty_api_secret.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import peewee
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
|
def migrate(migrator, db):
|
||||||
|
class CraftySettings(peewee.Model):
|
||||||
|
secret_api_key = peewee.CharField(default="")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
table_name = "crafty_settings"
|
||||||
|
|
||||||
|
migrator.create_table(CraftySettings)
|
||||||
|
|
||||||
|
|
||||||
|
def rollback(migrator, db):
|
||||||
|
migrator.drop_table("crafty_settings")
|
16
app/migrations/20220620_backup_shutdown.py
Normal file
16
app/migrations/20220620_backup_shutdown.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Generated by database migrator
|
||||||
|
import peewee
|
||||||
|
|
||||||
|
|
||||||
|
def migrate(migrator, database, **kwargs):
|
||||||
|
migrator.add_columns("backups", shutdown=peewee.BooleanField(default=False))
|
||||||
|
"""
|
||||||
|
Write your migrations here.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def rollback(migrator, database, **kwargs):
|
||||||
|
migrator.drop_columns("backups", ["shutdown"])
|
||||||
|
"""
|
||||||
|
Write your rollback migrations here.
|
||||||
|
"""
|
@ -352,7 +352,7 @@
|
|||||||
"createDirQuestion": "Welchen Namen wünschen Sie für das neue Verzeichnis?",
|
"createDirQuestion": "Welchen Namen wünschen Sie für das neue Verzeichnis?",
|
||||||
"createFile": "Datei erstellen",
|
"createFile": "Datei erstellen",
|
||||||
"createFileQuestion": "Welchen Namen wünschen Sie für die neue Datei?",
|
"createFileQuestion": "Welchen Namen wünschen Sie für die neue Datei?",
|
||||||
"default": "Standart",
|
"default": "Standard",
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
"deleteItemQuestion": "Sind Sie sicher, dass Sie \" + name + \" löschen wollen?",
|
"deleteItemQuestion": "Sind Sie sicher, dass Sie \" + name + \" löschen wollen?",
|
||||||
"deleteItemQuestionMessage": "Sie löschen gerade \\\"\" + path + \"\\\"!<br/><br/> Diese Aktion ist unumkehrbar!",
|
"deleteItemQuestionMessage": "Sie löschen gerade \\\"\" + path + \"\\\"!<br/><br/> Diese Aktion ist unumkehrbar!",
|
||||||
@ -454,7 +454,7 @@
|
|||||||
"buildServer": "Server erstellen!",
|
"buildServer": "Server erstellen!",
|
||||||
"clickRoot": "Hier klicken, um das root Verzeichniss auszuwählen",
|
"clickRoot": "Hier klicken, um das root Verzeichniss auszuwählen",
|
||||||
"close": "Schließen",
|
"close": "Schließen",
|
||||||
"defaultPort": "25565 (Standart)",
|
"defaultPort": "25565 (Standard)",
|
||||||
"downloading": "Server herunterladen...",
|
"downloading": "Server herunterladen...",
|
||||||
"explainRoot": "Bitte klicken Sie auf die Schaltfläche unterhalb, um das Stammverzeichnis Ihres Servers innerhalb des Archivs auszuwählen",
|
"explainRoot": "Bitte klicken Sie auf die Schaltfläche unterhalb, um das Stammverzeichnis Ihres Servers innerhalb des Archivs auszuwählen",
|
||||||
"importing": "Server importieren...",
|
"importing": "Server importieren...",
|
||||||
@ -529,4 +529,4 @@
|
|||||||
"userSettings": "Benutzereinstellungen",
|
"userSettings": "Benutzereinstellungen",
|
||||||
"uses": "Anzahl der erlaubten Verwendungen (-1==Keine Begrenzung)"
|
"uses": "Anzahl der erlaubten Verwendungen (-1==Keine Begrenzung)"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -271,7 +271,8 @@
|
|||||||
"save": "Save",
|
"save": "Save",
|
||||||
"size": "Size",
|
"size": "Size",
|
||||||
"storageLocation": "Storage Location",
|
"storageLocation": "Storage Location",
|
||||||
"storageLocationDesc": "Where do you want to store backups?"
|
"storageLocationDesc": "Where do you want to store backups?",
|
||||||
|
"shutdown": "Shutdown server for duration of backup"
|
||||||
},
|
},
|
||||||
"serverConfig": {
|
"serverConfig": {
|
||||||
"bePatientDelete": "Please be patient while we remove your server from the Crafty panel. This screen will close in a few moments.",
|
"bePatientDelete": "Please be patient while we remove your server from the Crafty panel. This screen will close in a few moments.",
|
||||||
@ -300,6 +301,9 @@
|
|||||||
"serverCrashDetection": "Server Crash Detection",
|
"serverCrashDetection": "Server Crash Detection",
|
||||||
"serverExecutable": "Server Executable",
|
"serverExecutable": "Server Executable",
|
||||||
"serverExecutableDesc": "The server's executable file",
|
"serverExecutableDesc": "The server's executable file",
|
||||||
|
"javaVersion": "Override current Java Version",
|
||||||
|
"javaVersionDesc": "If you're going to override java. Make sure your current java path in 'execution command' is wrapped in quotes (default 'java' variable excluded)",
|
||||||
|
"javaNoChange": "Do Not Override",
|
||||||
"serverExecutionCommand": "Server Execution Command",
|
"serverExecutionCommand": "Server Execution Command",
|
||||||
"serverExecutionCommandDesc": "What will be launched in a hidden terminal",
|
"serverExecutionCommandDesc": "What will be launched in a hidden terminal",
|
||||||
"serverIP": "Server IP",
|
"serverIP": "Server IP",
|
||||||
|
5
main.py
5
main.py
@ -7,6 +7,7 @@ import argparse
|
|||||||
import logging.config
|
import logging.config
|
||||||
import signal
|
import signal
|
||||||
import peewee
|
import peewee
|
||||||
|
from app.classes.shared.file_helpers import FileHelpers
|
||||||
|
|
||||||
from app.classes.shared.import3 import Import3
|
from app.classes.shared.import3 import Import3
|
||||||
from app.classes.shared.console import Console
|
from app.classes.shared.console import Console
|
||||||
@ -132,9 +133,9 @@ if __name__ == "__main__":
|
|||||||
installer.default_settings()
|
installer.default_settings()
|
||||||
else:
|
else:
|
||||||
Console.debug("Existing install detected")
|
Console.debug("Existing install detected")
|
||||||
|
file_helper = FileHelpers(helper)
|
||||||
# now the tables are created, we can load the tasks_manager and server controller
|
# now the tables are created, we can load the tasks_manager and server controller
|
||||||
controller = Controller(database, helper)
|
controller = Controller(database, helper, file_helper)
|
||||||
import3 = Import3(helper, controller)
|
import3 = Import3(helper, controller)
|
||||||
tasks_manager = TasksManager(helper, controller)
|
tasks_manager = TasksManager(helper, controller)
|
||||||
tasks_manager.start_webserver()
|
tasks_manager.start_webserver()
|
||||||
|
Loading…
Reference in New Issue
Block a user