Merge branch 'dev' into dev-BetterDisplayMobile

This commit is contained in:
Silversthorn 2022-05-18 22:13:44 +02:00
commit bef099a5a8
76 changed files with 5727 additions and 4700 deletions

View File

@ -6,6 +6,7 @@ docker-compose.yml
# git & gitlab related
.git/
.gitlab/
.gitignore
.gitlab-ci.yml

View File

@ -220,7 +220,7 @@ win-dev-build:
--collect-all six
# Download latest:
# | https://gitlab.com/crafty-controller/crafty-commander/-/jobs/artifacts/dev/download?job=win-dev-build
# | https://gitlab.com/crafty-controller/crafty-4/-/jobs/artifacts/dev/download?job=win-dev-build
artifacts:
name: "crafty-${CI_RUNNER_TAGS}-${CI_COMMIT_BRANCH}_${CI_COMMIT_SHORT_SHA}"
paths:
@ -261,7 +261,7 @@ win-prod-build:
--collect-all six
# Download latest:
# | https://gitlab.com/crafty-controller/crafty-commander/-/jobs/artifacts/master/download?job=win-prod-build
# | https://gitlab.com/crafty-controller/crafty-4/-/jobs/artifacts/master/download?job=win-prod-build
artifacts:
name: "crafty-${CI_RUNNER_TAGS}-${CI_COMMIT_BRANCH}_${CI_COMMIT_SHORT_SHA}"
paths:

View File

@ -94,7 +94,6 @@ disable=C0330,
fixme,
import-error,
inconsistent-return-statements,
invalid-name,
locally-disabled,
logging-format-interpolation,
logging-fstring-interpolation,
@ -236,10 +235,20 @@ function-naming-style=snake_case
#function-rgx=
# Good variable names which should always be accepted, separated by a comma.
good-names=i,
good-names=e,
ex,
f,
i,
id,
ip,
j,
k,
ex,
p,
r,
rs,
s,
tz,
v,
Run,
_

View File

@ -7,8 +7,8 @@ ENV LOG4J_FORMAT_MSG_NO_LOOKUPS=true
# Create non-root user & required dirs
RUN useradd -g root -M crafty \
&& mkdir /commander \
&& chown -R crafty:root /commander
&& mkdir /crafty \
&& chown -R crafty:root /crafty
# Install required system packages
RUN apt-get update \
@ -30,7 +30,7 @@ RUN apt-get update \
# Switch to service user for installing crafty deps
USER crafty
WORKDIR /commander
WORKDIR /crafty
COPY --chown=crafty:root requirements.txt ./
RUN python3 -m venv ./.venv \
&& . .venv/bin/activate \
@ -51,8 +51,8 @@ EXPOSE 8443
EXPOSE 19132
EXPOSE 25500-25600
# Start Crafty Commander through wrapper
ENTRYPOINT ["/commander/docker_launcher.sh"]
# Start Crafty through wrapper
ENTRYPOINT ["/crafty/docker_launcher.sh"]
CMD ["-v", "-d", "-i"]
# Add meta labels
@ -68,6 +68,6 @@ LABEL \
org.opencontainers.image.description="A Game Server Control Panel / Launcher" \
org.opencontainers.image.url="https://craftycontrol.com/" \
org.opencontainers.image.documentation="https://wiki.craftycontrol.com/" \
org.opencontainers.image.source="https://gitlab.com/crafty-controller/crafty-commander" \
org.opencontainers.image.source="https://gitlab.com/crafty-controller/crafty-4" \
org.opencontainers.image.vendor="Arcadia Technology, LLC." \
org.opencontainers.image.licenses=""
org.opencontainers.image.licenses="GPL-3.0"

View File

@ -2,11 +2,11 @@
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Supported Python Versions](https://shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20-blue)](https://www.python.org)
[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.0--alpha3.5-orange)](https://gitlab.com/crafty-controller/crafty-commander)
[![Code Quality(temp-hardcoded)](https://img.shields.io/badge/code%20quality-10-brightgreen)](https://gitlab.com/crafty-controller/crafty-commander)
[![Build Status](https://gitlab.com/crafty-controller/crafty-commander/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-commander/-/commits/master)
[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.0--beta-orange)](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)
# Crafty Controller 4.0.0-alpha.3.5
# Crafty Controller 4.0.0-beta
> Python based Control Panel for your Minecraft Server
## What is Crafty Controller?
@ -23,7 +23,7 @@ Project Homepage - https://craftycontrol.com
Discord Server - https://discord.gg/9VJPhCE
Git Repository - https://gitlab.com/crafty-controller/crafty-web
Git Repository - https://gitlab.com/crafty-controller/crafty-4
<br>
@ -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>
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>
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-commander/-/jobs/artifacts/master/download?job=win-prod-build), [Latest-Development](https://gitlab.com/crafty-controller/crafty-commander/-/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/-/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)
----
@ -55,35 +55,16 @@ As the Dockerfile uses the permission structure of `crafty:root` **internally**
### - Using the registry image 🌎
The provided image supports both `arm64` and `amd64` out the box, if you have issues though you can build it yourself with the `compose` file in `docker/`.
The image is located at: `registry.gitlab.com/crafty-controller/crafty-commander:latest`
The image is located at: `registry.gitlab.com/crafty-controller/crafty-4:latest`
| Branch | Status |
| ----------------- | ------------------------------------------------------------------ |
| :latest | [![pipeline status](https://gitlab.com/crafty-controller/crafty-commander/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-commander/-/commits/master) |
| :dev | [![pipeline status](https://gitlab.com/crafty-controller/crafty-commander/badges/dev/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-commander/-/commits/dev) |
| :latest | [![pipeline status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master) |
| :dev | [![pipeline status](https://gitlab.com/crafty-controller/crafty-4/badges/dev/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/dev)
While the repository is still **private / pre-release**,
Before you can pull the image you must authenticate docker with the Container Registry.
<br>
To authenticate you will need a [personal access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html)
with the minimum scope:
**Here are some example methods for getting started🚀:**
- For read (*pull*) access, `read_registry`.
- For write (*push*) access, `write_registry`.
When you have this just run:
```bash
$ docker login registry.gitlab.com -u <username> -p <token>
```
or
```bash
$ echo <token> | docker login registry.gitlab.com -u <username> --password-stdin
```
or
```bash
$ cat ~/my_password.txt | docker login registry.gitlab.com -u <username> --password-stdin
```
Then use one of the following methods:
### **docker-compose.yml:**
```sh
# Make your compose file
@ -94,8 +75,9 @@ version: '3'
services:
crafty:
container_name: crafty_commander
image: registry.gitlab.com/crafty-controller/crafty-commander:latest
container_name: crafty_container
image: registry.gitlab.com/crafty-controller/crafty-4:latest
restart: always
environment:
- TZ=Etc/UTC
ports:
@ -105,11 +87,11 @@ services:
- "19132:19132/udp" # BEDROCK
- "25500-25600:25500-25600" # MC SERV PORT RANGE
volumes:
- ./docker/backups:/commander/backups
- ./docker/logs:/commander/logs
- ./docker/servers:/commander/servers
- ./docker/config:/commander/app/config
- ./docker/import:/commander/import
- ./docker/backups:/crafty/backups
- ./docker/logs:/crafty/logs
- ./docker/servers:/crafty/servers
- ./docker/config:/crafty/app/config
- ./docker/import:/crafty/import
```
```sh
$ docker-compose up -d && docker-compose logs -f
@ -119,19 +101,21 @@ $ docker-compose up -d && docker-compose logs -f
### **docker run:**
```sh
$ docker run \
--name crafty_commander \
--name crafty_container \
--detach \
--restart always \
-p 8000:8000 \
-p 8443:8443 \
-p 8123:8123 \
-p 19132:19132/udp \
-p 25500-25600:25500-25600 \
-e TZ=Etc/UTC \
-v "/$(pwd)/docker/backups:/commander/backups" \
-v "/$(pwd)/docker/logs:/commander/logs" \
-v "/$(pwd)/docker/servers:/commander/servers" \
-v "/$(pwd)/docker/config:/commander/app/config" \
-v "/$(pwd)/docker/import:/commander/import" \
registry.gitlab.com/crafty-controller/crafty-commander:latest
-v "/$(pwd)/docker/backups:/crafty/backups" \
-v "/$(pwd)/docker/logs:/crafty/logs" \
-v "/$(pwd)/docker/servers:/crafty/servers" \
-v "/$(pwd)/docker/config:/crafty/app/config" \
-v "/$(pwd)/docker/import:/crafty/import" \
registry.gitlab.com/crafty-controller/crafty-4:latest
```
### **Building from the cloned repository:**
@ -144,18 +128,20 @@ If you'd rather not use `docker-compose` you can use the following `docker run`
$ docker build . -t crafty
$ docker run \
--name crafty_commander \
--name crafty_container \
--detach \
--restart always \
-p 8000:8000 \
-p 8443:8443 \
-p 8123:8123 \
-p 19132:19132/udp \
-p 25500-25600:25500-25600 \
-e TZ=Etc/UTC \
-v "/$(pwd)/docker/backups:/commander/backups" \
-v "/$(pwd)/docker/logs:/commander/logs" \
-v "/$(pwd)/docker/servers:/commander/servers" \
-v "/$(pwd)/docker/config:/commander/app/config" \
-v "/$(pwd)/docker/import:/commander/import" \
-v "/$(pwd)/docker/backups:/crafty/backups" \
-v "/$(pwd)/docker/logs:/crafty/logs" \
-v "/$(pwd)/docker/servers:/crafty/servers" \
-v "/$(pwd)/docker/config:/crafty/app/config" \
-v "/$(pwd)/docker/import:/crafty/import" \
crafty
```
A fresh build will take several minutes depending on your system, but will be rapid thereafter.

View File

@ -1,37 +1,37 @@
import logging
from app.classes.models.crafty_permissions import (
crafty_permissions,
Enum_Permissions_Crafty,
PermissionsCrafty,
EnumPermissionsCrafty,
)
from app.classes.models.users import ApiKeys
logger = logging.getLogger(__name__)
class Crafty_Perms_Controller:
class CraftyPermsController:
@staticmethod
def list_defined_crafty_permissions():
permissions_list = crafty_permissions.get_permissions_list()
permissions_list = PermissionsCrafty.get_permissions_list()
return permissions_list
@staticmethod
def get_mask_crafty_permissions(user_id):
permissions_mask = crafty_permissions.get_crafty_permissions_mask(user_id)
permissions_mask = PermissionsCrafty.get_crafty_permissions_mask(user_id)
return permissions_mask
@staticmethod
def set_permission(
permission_mask, permission_tested: Enum_Permissions_Crafty, value
permission_mask, permission_tested: EnumPermissionsCrafty, value
):
return crafty_permissions.set_permission(
return PermissionsCrafty.set_permission(
permission_mask, permission_tested, value
)
@staticmethod
def can_create_server(user_id):
return crafty_permissions.can_add_in_crafty(
user_id, Enum_Permissions_Crafty.Server_Creation
return PermissionsCrafty.can_add_in_crafty(
user_id, EnumPermissionsCrafty.SERVER_CREATION
)
@staticmethod
@ -52,22 +52,22 @@ class Crafty_Perms_Controller:
@staticmethod
def list_all_crafty_permissions_quantity_limits():
return crafty_permissions.get_all_permission_quantity_list()
return PermissionsCrafty.get_all_permission_quantity_list()
@staticmethod
def list_crafty_permissions_quantity_limits(user_id):
return crafty_permissions.get_permission_quantity_list(user_id)
return PermissionsCrafty.get_permission_quantity_list(user_id)
@staticmethod
def get_crafty_permissions_list(user_id):
permissions_mask = crafty_permissions.get_crafty_permissions_mask(user_id)
permissions_list = crafty_permissions.get_permissions(permissions_mask)
permissions_mask = PermissionsCrafty.get_crafty_permissions_mask(user_id)
permissions_list = PermissionsCrafty.get_permissions(permissions_mask)
return permissions_list
@staticmethod
def add_server_creation(user_id):
return crafty_permissions.add_server_creation(user_id)
return PermissionsCrafty.add_server_creation(user_id)
@staticmethod
def get_api_key_permissions_list(key: ApiKeys):
return crafty_permissions.get_api_key_permissions_list(key)
return PermissionsCrafty.get_api_key_permissions_list(key)

View File

@ -1,60 +1,59 @@
import logging
from app.classes.models.management import management_helper
from app.classes.models.servers import servers_helper
from app.classes.models.management import HelpersManagement
from app.classes.models.servers import HelperServers
logger = logging.getLogger(__name__)
class Management_Controller:
class ManagementController:
def __init__(self, management_helper):
self.management_helper = management_helper
# **********************************************************************************
# Host_Stats Methods
# **********************************************************************************
@staticmethod
def get_latest_hosts_stats():
return management_helper.get_latest_hosts_stats()
return HelpersManagement.get_latest_hosts_stats()
# **********************************************************************************
# Commands Methods
# **********************************************************************************
@staticmethod
def get_unactioned_commands():
return management_helper.get_unactioned_commands()
return HelpersManagement.get_unactioned_commands()
@staticmethod
def send_command(user_id, server_id, remote_ip, command):
server_name = servers_helper.get_server_friendly_name(server_id)
def send_command(self, user_id, server_id, remote_ip, command):
server_name = HelperServers.get_server_friendly_name(server_id)
# Example: Admin issued command start_server for server Survival
management_helper.add_to_audit_log(
self.management_helper.add_to_audit_log(
user_id,
f"issued command {command} for server {server_name}",
server_id,
remote_ip,
)
management_helper.add_command(server_id, user_id, remote_ip, command)
HelpersManagement.add_command(server_id, user_id, remote_ip, command)
@staticmethod
def mark_command_complete(command_id=None):
return management_helper.mark_command_complete(command_id)
return HelpersManagement.mark_command_complete(command_id)
# **********************************************************************************
# Audit_Log Methods
# **********************************************************************************
@staticmethod
def get_actity_log():
return management_helper.get_actity_log()
return HelpersManagement.get_actity_log()
@staticmethod
def add_to_audit_log(user_id, log_msg, server_id=None, source_ip=None):
return management_helper.add_to_audit_log(
def add_to_audit_log(self, user_id, log_msg, server_id=None, source_ip=None):
return self.management_helper.add_to_audit_log(
user_id, log_msg, server_id, source_ip
)
@staticmethod
def add_to_audit_log_raw(user_name, user_id, server_id, log_msg, source_ip):
return management_helper.add_to_audit_log_raw(
def add_to_audit_log_raw(self, user_name, user_id, server_id, log_msg, source_ip):
return self.management_helper.add_to_audit_log_raw(
user_name, user_id, server_id, log_msg, source_ip
)
@ -72,7 +71,7 @@ class Management_Controller:
comment=None,
enabled=True,
):
return management_helper.create_scheduled_task(
return HelpersManagement.create_scheduled_task(
server_id,
action,
interval,
@ -85,63 +84,61 @@ class Management_Controller:
@staticmethod
def delete_scheduled_task(schedule_id):
return management_helper.delete_scheduled_task(schedule_id)
return HelpersManagement.delete_scheduled_task(schedule_id)
@staticmethod
def update_scheduled_task(schedule_id, updates):
return management_helper.update_scheduled_task(schedule_id, updates)
return HelpersManagement.update_scheduled_task(schedule_id, updates)
@staticmethod
def get_scheduled_task(schedule_id):
return management_helper.get_scheduled_task(schedule_id)
return HelpersManagement.get_scheduled_task(schedule_id)
@staticmethod
def get_scheduled_task_model(schedule_id):
return management_helper.get_scheduled_task_model(schedule_id)
return HelpersManagement.get_scheduled_task_model(schedule_id)
@staticmethod
def get_child_schedules(sch_id):
return management_helper.get_child_schedules(sch_id)
return HelpersManagement.get_child_schedules(sch_id)
@staticmethod
def get_schedules_by_server(server_id):
return management_helper.get_schedules_by_server(server_id)
return HelpersManagement.get_schedules_by_server(server_id)
@staticmethod
def get_schedules_all():
return management_helper.get_schedules_all()
return HelpersManagement.get_schedules_all()
@staticmethod
def get_schedules_enabled():
return management_helper.get_schedules_enabled()
return HelpersManagement.get_schedules_enabled()
# **********************************************************************************
# Backups Methods
# **********************************************************************************
@staticmethod
def get_backup_config(server_id):
return management_helper.get_backup_config(server_id)
return HelpersManagement.get_backup_config(server_id)
@staticmethod
def set_backup_config(
self,
server_id: int,
backup_path: str = None,
max_backups: int = None,
excluded_dirs: list = None,
compress: bool = False,
):
return management_helper.set_backup_config(
return self.management_helper.set_backup_config(
server_id, backup_path, max_backups, excluded_dirs, compress
)
@staticmethod
def get_excluded_backup_dirs(server_id: int):
return management_helper.get_excluded_backup_dirs(server_id)
return HelpersManagement.get_excluded_backup_dirs(server_id)
@staticmethod
def add_excluded_backup_dir(server_id: int, dir_to_add: str):
management_helper.add_excluded_backup_dir(server_id, dir_to_add)
def add_excluded_backup_dir(self, server_id: int, dir_to_add: str):
self.management_helper.add_excluded_backup_dir(server_id, dir_to_add)
@staticmethod
def del_excluded_backup_dir(server_id: int, dir_to_del: str):
management_helper.del_excluded_backup_dir(server_id, dir_to_del)
def del_excluded_backup_dir(self, server_id: int, dir_to_del: str):
self.management_helper.del_excluded_backup_dir(server_id, dir_to_del)

View File

@ -1,31 +1,34 @@
import logging
from app.classes.models.roles import roles_helper
from app.classes.models.server_permissions import server_permissions
from app.classes.models.users import users_helper
from app.classes.shared.helpers import helper
from app.classes.models.roles import HelperRoles
from app.classes.models.server_permissions import PermissionsServers
from app.classes.shared.helpers import Helpers
logger = logging.getLogger(__name__)
class Roles_Controller:
class RolesController:
def __init__(self, users_helper, roles_helper):
self.users_helper = users_helper
self.roles_helper = roles_helper
@staticmethod
def get_all_roles():
return roles_helper.get_all_roles()
return HelperRoles.get_all_roles()
@staticmethod
def get_roleid_by_name(role_name):
return roles_helper.get_roleid_by_name(role_name)
return HelperRoles.get_roleid_by_name(role_name)
@staticmethod
def get_role(role_id):
return roles_helper.get_role(role_id)
return HelperRoles.get_role(role_id)
@staticmethod
def update_role(role_id: str, role_data=None, permissions_mask: str = "00000000"):
if role_data is None:
role_data = {}
base_data = Roles_Controller.get_role_with_servers(role_id)
base_data = RolesController.get_role_with_servers(role_id)
up_data = {}
added_servers = set()
removed_servers = set()
@ -37,41 +40,40 @@ class Roles_Controller:
removed_servers = base_data["servers"].difference(role_data["servers"])
elif base_data[key] != role_data[key]:
up_data[key] = role_data[key]
up_data["last_update"] = helper.get_time_as_string()
up_data["last_update"] = Helpers.get_time_as_string()
logger.debug(
f"role: {role_data} +server:{added_servers} -server{removed_servers}"
)
for server in added_servers:
server_permissions.get_or_create(role_id, server, permissions_mask)
PermissionsServers.get_or_create(role_id, server, permissions_mask)
for server in base_data["servers"]:
server_permissions.update_role_permission(role_id, server, permissions_mask)
PermissionsServers.update_role_permission(role_id, server, permissions_mask)
# TODO: This is horribly inefficient and we should be using bulk queries
# but im going for functionality at this point
server_permissions.delete_roles_permissions(role_id, removed_servers)
PermissionsServers.delete_roles_permissions(role_id, removed_servers)
if up_data:
roles_helper.update_role(role_id, up_data)
HelperRoles.update_role(role_id, up_data)
@staticmethod
def add_role(role_name):
return roles_helper.add_role(role_name)
return HelperRoles.add_role(role_name)
@staticmethod
def remove_role(role_id):
role_data = Roles_Controller.get_role_with_servers(role_id)
server_permissions.delete_roles_permissions(role_id, role_data["servers"])
users_helper.remove_roles_from_role_id(role_id)
return roles_helper.remove_role(role_id)
def remove_role(self, role_id):
role_data = RolesController.get_role_with_servers(role_id)
PermissionsServers.delete_roles_permissions(role_id, role_data["servers"])
self.users_helper.remove_roles_from_role_id(role_id)
return self.roles_helper.remove_role(role_id)
@staticmethod
def role_id_exists(role_id):
return roles_helper.role_id_exists(role_id)
return HelperRoles.role_id_exists(role_id)
@staticmethod
def get_role_with_servers(role_id):
role = roles_helper.get_role(role_id)
role = HelperRoles.get_role(role_id)
if role:
servers_query = server_permissions.get_servers_from_role(role_id)
servers_query = PermissionsServers.get_servers_from_role(role_id)
# TODO: this query needs to be narrower
servers = set()
for s in servers_query:

View File

@ -1,114 +1,122 @@
import logging
from app.classes.models.server_permissions import (
server_permissions,
Enum_Permissions_Server,
PermissionsServers,
EnumPermissionsServer,
)
from app.classes.models.users import users_helper, ApiKeys
from app.classes.models.roles import roles_helper
from app.classes.models.servers import servers_helper
from app.classes.shared.main_models import db_helper
from app.classes.models.users import HelperUsers, ApiKeys
from app.classes.models.roles import HelperRoles
from app.classes.models.servers import HelperServers
from app.classes.models.server_stats import HelperServerStats
from app.classes.shared.main_models import DatabaseShortcuts
logger = logging.getLogger(__name__)
class Server_Perms_Controller:
class ServerPermsController:
@staticmethod
def get_server_user_list(server_id):
return server_permissions.get_server_user_list(server_id)
return PermissionsServers.get_server_user_list(server_id)
@staticmethod
def list_defined_permissions():
permissions_list = server_permissions.get_permissions_list()
permissions_list = PermissionsServers.get_permissions_list()
return permissions_list
@staticmethod
def get_mask_permissions(role_id, server_id):
permissions_mask = server_permissions.get_permissions_mask(role_id, server_id)
permissions_mask = PermissionsServers.get_permissions_mask(role_id, server_id)
return permissions_mask
@staticmethod
def get_role_permissions(role_id):
permissions_list = server_permissions.get_role_permissions_list(role_id)
permissions_list = PermissionsServers.get_role_permissions_list(role_id)
return permissions_list
@staticmethod
def add_role_server(server_id, role_id, rs_permissions="00000000"):
return server_permissions.add_role_server(server_id, role_id, rs_permissions)
return PermissionsServers.add_role_server(server_id, role_id, rs_permissions)
@staticmethod
def get_server_roles(server_id):
return server_permissions.get_server_roles(server_id)
return PermissionsServers.get_server_roles(server_id)
@staticmethod
def backup_role_swap(old_server_id, new_server_id):
role_list = server_permissions.get_server_roles(old_server_id)
role_list = PermissionsServers.get_server_roles(old_server_id)
for role in role_list:
server_permissions.add_role_server(
PermissionsServers.add_role_server(
new_server_id,
role.role_id,
server_permissions.get_permissions_mask(
PermissionsServers.get_permissions_mask(
int(role.role_id), int(old_server_id)
),
)
# server_permissions.add_role_server(new_server_id, role.role_id,"00001000")
# Permissions_Servers.add_role_server(
# new_server_id, role.role_id, "00001000"
# )
# **********************************************************************************
# Servers Permissions Methods
# **********************************************************************************
@staticmethod
def get_permissions_mask(role_id, server_id):
return server_permissions.get_permissions_mask(role_id, server_id)
return PermissionsServers.get_permissions_mask(role_id, server_id)
@staticmethod
def set_permission(
permission_mask, permission_tested: Enum_Permissions_Server, value
permission_mask, permission_tested: EnumPermissionsServer, value
):
return server_permissions.set_permission(
return PermissionsServers.set_permission(
permission_mask, permission_tested, value
)
@staticmethod
def get_role_permissions_list(role_id):
return server_permissions.get_role_permissions_list(role_id)
return PermissionsServers.get_role_permissions_list(role_id)
@staticmethod
def get_user_id_permissions_list(user_id: str, server_id: str):
return server_permissions.get_user_id_permissions_list(user_id, server_id)
return PermissionsServers.get_user_id_permissions_list(user_id, server_id)
@staticmethod
def get_api_key_id_permissions_list(key_id: str, server_id: str):
key = users_helper.get_user_api_key(key_id)
return server_permissions.get_api_key_permissions_list(key, server_id)
key = HelperUsers.get_user_api_key(key_id)
return PermissionsServers.get_api_key_permissions_list(key, server_id)
@staticmethod
def get_api_key_permissions_list(key: ApiKeys, server_id: str):
return server_permissions.get_api_key_permissions_list(key, server_id)
return PermissionsServers.get_api_key_permissions_list(key, server_id)
@staticmethod
def get_authorized_servers_stats_from_roles(user_id):
user_roles = users_helper.get_user_roles_id(user_id)
user_roles = HelperUsers.get_user_roles_id(user_id)
roles_list = []
role_server = []
authorized_servers = []
server_data = []
for u in user_roles:
roles_list.append(roles_helper.get_role(u.role_id))
for user in user_roles:
roles_list.append(HelperRoles.get_role(user.role_id))
for r in roles_list:
role_test = server_permissions.get_role_servers_from_role_id(
r.get("role_id")
for role in roles_list:
role_test = PermissionsServers.get_role_servers_from_role_id(
role.get("role_id")
)
for t in role_test:
role_server.append(t)
for test in role_test:
role_server.append(test)
for s in role_server:
authorized_servers.append(servers_helper.get_server_data_by_id(s.server_id))
for server in role_server:
authorized_servers.append(
HelperServers.get_server_data_by_id(server.server_id)
)
for s in authorized_servers:
latest = servers_helper.get_latest_server_stats(s.get("server_id"))
for server in authorized_servers:
latest = HelperServerStats.get_latest_server_stats(server.get("server_id"))
server_data.append(
{"server_data": s, "stats": db_helper.return_rows(latest)[0]}
{
"server_data": server,
"stats": DatabaseShortcuts.return_rows(latest)[0],
}
)
return server_data

View File

@ -2,26 +2,29 @@ import os
import logging
import json
from app.classes.controllers.roles_controller import Roles_Controller
from app.classes.models.servers import servers_helper
from app.classes.models.users import users_helper, ApiKeys
from app.classes.controllers.roles_controller import RolesController
from app.classes.models.servers import HelperServers
from app.classes.models.server_stats import HelperServerStats
from app.classes.models.users import HelperUsers, ApiKeys
from app.classes.models.server_permissions import (
server_permissions,
Enum_Permissions_Server,
PermissionsServers,
EnumPermissionsServer,
)
from app.classes.shared.helpers import helper
from app.classes.shared.main_models import db_helper
from app.classes.shared.helpers import Helpers
from app.classes.shared.main_models import DatabaseShortcuts
logger = logging.getLogger(__name__)
class Servers_Controller:
class ServersController:
def __init__(self, servers_helper):
self.servers_helper = servers_helper
# **********************************************************************************
# Generic Servers Methods
# **********************************************************************************
@staticmethod
def create_server(
self,
name: str,
server_uuid: str,
server_dir: str,
@ -33,7 +36,7 @@ class Servers_Controller:
server_type: str,
server_port=25565,
):
return servers_helper.create_server(
return self.servers_helper.create_server(
name,
server_uuid,
server_dir,
@ -48,81 +51,82 @@ class Servers_Controller:
@staticmethod
def get_server_obj(server_id):
return servers_helper.get_server_obj(server_id)
return HelperServers.get_server_obj(server_id)
@staticmethod
def update_server(server_obj):
return servers_helper.update_server(server_obj)
return HelperServers.update_server(server_obj)
@staticmethod
def set_download(server_id):
return servers_helper.set_download(server_id)
return HelperServerStats.set_download(server_id)
@staticmethod
def finish_download(server_id):
return servers_helper.finish_download(server_id)
return HelperServerStats.finish_download(server_id)
@staticmethod
def get_download_status(server_id):
return servers_helper.get_download_status(server_id)
return HelperServerStats.get_download_status(server_id)
@staticmethod
def remove_server(server_id):
roles_list = server_permissions.get_roles_from_server(server_id)
def remove_server(self, server_id):
roles_list = PermissionsServers.get_roles_from_server(server_id)
for role in roles_list:
role_id = role.role_id
role_data = Roles_Controller.get_role_with_servers(role_id)
role_data = RolesController.get_role_with_servers(role_id)
role_data["servers"] = {server_id}
server_permissions.delete_roles_permissions(role_id, role_data["servers"])
server_permissions.remove_roles_of_server(server_id)
servers_helper.remove_server(server_id)
PermissionsServers.delete_roles_permissions(role_id, role_data["servers"])
PermissionsServers.remove_roles_of_server(server_id)
self.servers_helper.remove_server(server_id)
@staticmethod
def get_server_data_by_id(server_id):
return servers_helper.get_server_data_by_id(server_id)
return HelperServers.get_server_data_by_id(server_id)
# **********************************************************************************
# Servers Methods
# **********************************************************************************
@staticmethod
def get_all_defined_servers():
return servers_helper.get_all_defined_servers()
return HelperServers.get_all_defined_servers()
@staticmethod
def get_authorized_servers(user_id):
server_data = []
user_roles = users_helper.user_role_query(user_id)
for us in user_roles:
role_servers = server_permissions.get_role_servers_from_role_id(us.role_id)
user_roles = HelperUsers.user_role_query(user_id)
for user in user_roles:
role_servers = PermissionsServers.get_role_servers_from_role_id(
user.role_id
)
for role in role_servers:
server_data.append(servers_helper.get_server_data_by_id(role.server_id))
server_data.append(HelperServers.get_server_data_by_id(role.server_id))
return server_data
@staticmethod
def get_all_servers_stats():
return servers_helper.get_all_servers_stats()
return HelperServerStats.get_all_servers_stats()
@staticmethod
def get_authorized_servers_stats_api_key(api_key: ApiKeys):
server_data = []
authorized_servers = Servers_Controller.get_authorized_servers(
authorized_servers = ServersController.get_authorized_servers(
api_key.user.user_id
)
for s in authorized_servers:
latest = servers_helper.get_latest_server_stats(s.get("server_id"))
key_permissions = server_permissions.get_api_key_permissions_list(
api_key, s.get("server_id")
for server in authorized_servers:
latest = HelperServerStats.get_latest_server_stats(server.get("server_id"))
key_permissions = PermissionsServers.get_api_key_permissions_list(
api_key, server.get("server_id")
)
if Enum_Permissions_Server.Commands in key_permissions:
if EnumPermissionsServer.COMMANDS in key_permissions:
user_command_permission = True
else:
user_command_permission = False
server_data.append(
{
"server_data": s,
"stats": db_helper.return_rows(latest)[0],
"server_data": server,
"stats": DatabaseShortcuts.return_rows(latest)[0],
"user_command_permission": user_command_permission,
}
)
@ -131,22 +135,22 @@ class Servers_Controller:
@staticmethod
def get_authorized_servers_stats(user_id):
server_data = []
authorized_servers = Servers_Controller.get_authorized_servers(user_id)
authorized_servers = ServersController.get_authorized_servers(user_id)
for s in authorized_servers:
latest = servers_helper.get_latest_server_stats(s.get("server_id"))
for server in authorized_servers:
latest = HelperServerStats.get_latest_server_stats(server.get("server_id"))
# TODO
user_permissions = server_permissions.get_user_id_permissions_list(
user_id, s.get("server_id")
user_permissions = PermissionsServers.get_user_id_permissions_list(
user_id, server.get("server_id")
)
if Enum_Permissions_Server.Commands in user_permissions:
if EnumPermissionsServer.COMMANDS in user_permissions:
user_command_permission = True
else:
user_command_permission = False
server_data.append(
{
"server_data": s,
"stats": db_helper.return_rows(latest)[0],
"server_data": server,
"stats": DatabaseShortcuts.return_rows(latest)[0],
"user_command_permission": user_command_permission,
}
)
@ -155,28 +159,28 @@ class Servers_Controller:
@staticmethod
def get_server_friendly_name(server_id):
return servers_helper.get_server_friendly_name(server_id)
return HelperServers.get_server_friendly_name(server_id)
# **********************************************************************************
# Servers_Stats Methods
# **********************************************************************************
@staticmethod
def get_server_stats_by_id(server_id):
return servers_helper.get_server_stats_by_id(server_id)
return HelperServerStats.get_server_stats_by_id(server_id)
@staticmethod
def server_id_exists(server_id):
return servers_helper.server_id_exists(server_id)
return HelperServerStats.server_id_exists(server_id)
@staticmethod
def get_server_type_by_id(server_id):
return servers_helper.get_server_type_by_id(server_id)
return HelperServers.get_server_type_by_id(server_id)
@staticmethod
def server_id_authorized(server_id_a, user_id):
user_roles = users_helper.user_role_query(user_id)
user_roles = HelperUsers.user_role_query(user_id)
for role in user_roles:
for server_id_b in server_permissions.get_role_servers_from_role_id(
for server_id_b in PermissionsServers.get_role_servers_from_role_id(
role.role_id
):
if str(server_id_a) == str(server_id_b.server_id):
@ -185,51 +189,51 @@ class Servers_Controller:
@staticmethod
def is_crashed(server_id):
return servers_helper.is_crashed(server_id)
return HelperServerStats.is_crashed(server_id)
@staticmethod
def server_id_authorized_api_key(server_id: str, api_key: ApiKeys) -> bool:
# TODO
return Servers_Controller.server_id_authorized(server_id, api_key.user.user_id)
return ServersController.server_id_authorized(server_id, api_key.user.user_id)
# There is no view server permission
# permission_helper.both_have_perm(api_key)
@staticmethod
def set_update(server_id, value):
return servers_helper.set_update(server_id, value)
return HelperServerStats.set_update(server_id, value)
@staticmethod
def get_TTL_without_player(server_id):
return servers_helper.get_TTL_without_player(server_id)
def get_ttl_without_player(server_id):
return HelperServerStats.get_ttl_without_player(server_id)
@staticmethod
def can_stop_no_players(server_id, time_limit):
return servers_helper.can_stop_no_players(server_id, time_limit)
return HelperServerStats.can_stop_no_players(server_id, time_limit)
@staticmethod
def set_waiting_start(server_id, value):
servers_helper.set_waiting_start(server_id, value)
HelperServerStats.set_waiting_start(server_id, value)
@staticmethod
def get_waiting_start(server_id):
return servers_helper.get_waiting_start(server_id)
return HelperServerStats.get_waiting_start(server_id)
@staticmethod
def get_update_status(server_id):
return servers_helper.get_update_status(server_id)
return HelperServerStats.get_update_status(server_id)
# **********************************************************************************
# Servers Helpers Methods
# **********************************************************************************
@staticmethod
def get_banned_players(server_id):
stats = servers_helper.get_server_stats_by_id(server_id)
stats = HelperServerStats.get_server_stats_by_id(server_id)
server_path = stats["server_id"]["path"]
path = os.path.join(server_path, "banned-players.json")
try:
with open(
helper.get_os_understandable_path(path), encoding="utf-8"
Helpers.get_os_understandable_path(path), encoding="utf-8"
) as file:
content = file.read()
file.close()
@ -240,7 +244,7 @@ class Servers_Controller:
return json.loads(content)
def check_for_old_logs(self):
servers = servers_helper.get_all_defined_servers()
servers = HelperServers.get_all_defined_servers()
for server in servers:
logs_path = os.path.split(server["log_path"])[0]
latest_log_file = os.path.split(server["log_path"])[1]
@ -253,9 +257,9 @@ class Servers_Controller:
)
for log_file in log_files:
log_file_path = os.path.join(logs_path, log_file)
if helper.check_file_exists(
if Helpers.check_file_exists(
log_file_path
) and helper.is_file_older_than_x_days(
) and Helpers.is_file_older_than_x_days(
log_file_path, logs_delete_after
):
os.remove(log_file_path)

View File

@ -1,61 +1,62 @@
import logging
from typing import Optional
from app.classes.models.users import users_helper
from app.classes.models.users import HelperUsers
from app.classes.models.crafty_permissions import (
crafty_permissions,
Enum_Permissions_Crafty,
PermissionsCrafty,
EnumPermissionsCrafty,
)
from app.classes.shared.helpers import helper
from app.classes.shared.authentication import authentication
logger = logging.getLogger(__name__)
class Users_Controller:
class UsersController:
def __init__(self, helper, users_helper, authentication):
self.helper = helper
self.users_helper = users_helper
self.authentication = authentication
# **********************************************************************************
# Users Methods
# **********************************************************************************
@staticmethod
def get_all_users():
return users_helper.get_all_users()
return HelperUsers.get_all_users()
@staticmethod
def get_id_by_name(username):
return users_helper.get_user_id_by_name(username)
return HelperUsers.get_user_id_by_name(username)
@staticmethod
def get_user_lang_by_id(user_id):
return users_helper.get_user_lang_by_id(user_id)
return HelperUsers.get_user_lang_by_id(user_id)
@staticmethod
def get_user_by_id(user_id):
return users_helper.get_user(user_id)
return HelperUsers.get_user(user_id)
@staticmethod
def update_server_order(user_id, user_server_order):
users_helper.update_server_order(user_id, user_server_order)
HelperUsers.update_server_order(user_id, user_server_order)
@staticmethod
def get_server_order(user_id):
return users_helper.get_server_order(user_id)
return HelperUsers.get_server_order(user_id)
@staticmethod
def user_query(user_id):
return users_helper.user_query(user_id)
return HelperUsers.user_query(user_id)
@staticmethod
def set_support_path(user_id, support_path):
users_helper.set_support_path(user_id, support_path)
HelperUsers.set_support_path(user_id, support_path)
@staticmethod
def update_user(user_id: str, user_data=None, user_crafty_data=None):
def update_user(self, user_id: str, user_data=None, user_crafty_data=None):
if user_crafty_data is None:
user_crafty_data = {}
if user_data is None:
user_data = {}
base_data = users_helper.get_user(user_id)
base_data = HelperUsers.get_user(user_id)
up_data = {}
added_roles = set()
removed_roles = set()
@ -67,53 +68,54 @@ class Users_Controller:
removed_roles = base_data["roles"].difference(user_data["roles"])
elif key == "password":
if user_data["password"] is not None and user_data["password"] != "":
up_data["password"] = helper.encode_pass(user_data["password"])
up_data["password"] = self.helper.encode_pass(user_data["password"])
elif base_data[key] != user_data[key]:
up_data[key] = user_data[key]
up_data["last_update"] = helper.get_time_as_string()
up_data["last_update"] = self.helper.get_time_as_string()
up_data["lang"] = user_data["lang"]
up_data["hints"] = user_data["hints"]
logger.debug(f"user: {user_data} +role:{added_roles} -role:{removed_roles}")
for role in added_roles:
users_helper.get_or_create(user_id=user_id, role_id=role)
permissions_mask = user_crafty_data.get("permissions_mask", "000")
HelperUsers.get_or_create(user_id=user_id, role_id=role)
permissions_mask = user_crafty_data.get("permissions_mask", "000")
if "server_quantity" in user_crafty_data:
limit_server_creation = user_crafty_data["server_quantity"][
Enum_Permissions_Crafty.Server_Creation.name
]
if "server_quantity" in user_crafty_data:
limit_server_creation = user_crafty_data["server_quantity"][
EnumPermissionsCrafty.SERVER_CREATION.name
]
limit_user_creation = user_crafty_data["server_quantity"][
Enum_Permissions_Crafty.User_Config.name
]
limit_role_creation = user_crafty_data["server_quantity"][
Enum_Permissions_Crafty.Roles_Config.name
]
else:
limit_server_creation = 0
limit_user_creation = 0
limit_role_creation = 0
limit_user_creation = user_crafty_data["server_quantity"][
EnumPermissionsCrafty.USER_CONFIG.name
]
limit_role_creation = user_crafty_data["server_quantity"][
EnumPermissionsCrafty.ROLES_CONFIG.name
]
else:
limit_server_creation = 0
limit_user_creation = 0
limit_role_creation = 0
crafty_permissions.add_or_update_user(
user_id,
permissions_mask,
limit_server_creation,
limit_user_creation,
limit_role_creation,
)
PermissionsCrafty.add_or_update_user(
user_id,
permissions_mask,
limit_server_creation,
limit_user_creation,
limit_role_creation,
)
users_helper.delete_user_roles(user_id, removed_roles)
self.users_helper.delete_user_roles(user_id, removed_roles)
users_helper.update_user(user_id, up_data)
self.users_helper.update_user(user_id, up_data)
@staticmethod
def add_user(
self,
username,
password,
email="default@example.com",
enabled: bool = True,
superuser: bool = False,
):
return users_helper.add_user(
return self.users_helper.add_user(
username,
password=password,
email=email,
@ -129,7 +131,7 @@ class Users_Controller:
enabled: bool = True,
superuser: bool = False,
):
return users_helper.add_rawpass_user(
return HelperUsers.add_rawpass_user(
username,
password=password,
email=email,
@ -137,55 +139,54 @@ class Users_Controller:
superuser=superuser,
)
@staticmethod
def remove_user(user_id):
return users_helper.remove_user(user_id)
def remove_user(self, user_id):
return self.users_helper.remove_user(user_id)
@staticmethod
def user_id_exists(user_id):
return users_helper.user_id_exists(user_id)
return HelperUsers.user_id_exists(user_id)
@staticmethod
def set_prepare(user_id):
return users_helper.set_prepare(user_id)
return HelperUsers.set_prepare(user_id)
@staticmethod
def stop_prepare(user_id):
return users_helper.stop_prepare(user_id)
return HelperUsers.stop_prepare(user_id)
@staticmethod
def get_user_id_by_api_token(token: str) -> str:
token_data = authentication.check_no_iat(token)
def get_user_id_by_api_token(self, token: str) -> str:
token_data = self.authentication.check_no_iat(token)
return token_data["user_id"]
@staticmethod
def get_user_by_api_token(token: str):
_, user = authentication.check(token)
def get_user_by_api_token(self, token: str):
_, _, user = self.authentication.check(token)
return user
def get_api_key_by_token(self, token: str):
key, _, _ = self.authentication.check(token)
return key
# **********************************************************************************
# User Roles Methods
# **********************************************************************************
@staticmethod
def get_user_roles_id(user_id):
return users_helper.get_user_roles_id(user_id)
return HelperUsers.get_user_roles_id(user_id)
@staticmethod
def get_user_roles_names(user_id):
return users_helper.get_user_roles_names(user_id)
return HelperUsers.get_user_roles_names(user_id)
@staticmethod
def add_role_to_user(user_id, role_id):
return users_helper.add_role_to_user(user_id, role_id)
def add_role_to_user(self, user_id, role_id):
return self.users_helper.add_role_to_user(user_id, role_id)
@staticmethod
def add_user_roles(user):
return users_helper.add_user_roles(user)
def add_user_roles(self, user):
return self.users_helper.add_user_roles(user)
@staticmethod
def user_role_query(user_id):
return users_helper.user_role_query(user_id)
return HelperUsers.user_role_query(user_id)
# **********************************************************************************
# Api Keys Methods
@ -193,28 +194,26 @@ class Users_Controller:
@staticmethod
def get_user_api_keys(user_id: str):
return users_helper.get_user_api_keys(user_id)
return HelperUsers.get_user_api_keys(user_id)
@staticmethod
def get_user_api_key(key_id: str):
return users_helper.get_user_api_key(key_id)
return HelperUsers.get_user_api_key(key_id)
@staticmethod
def add_user_api_key(
self,
name: str,
user_id: str,
superuser: bool = False,
server_permissions_mask: Optional[str] = None,
crafty_permissions_mask: Optional[str] = None,
):
return users_helper.add_user_api_key(
return self.users_helper.add_user_api_key(
name, user_id, superuser, server_permissions_mask, crafty_permissions_mask
)
@staticmethod
def delete_user_api_keys(user_id: str):
return users_helper.delete_user_api_keys(user_id)
def delete_user_api_keys(self, user_id: str):
return self.users_helper.delete_user_api_keys(user_id)
@staticmethod
def delete_user_api_key(key_id: str):
return users_helper.delete_user_api_key(key_id)
def delete_user_api_key(self, key_id: str):
return self.users_helper.delete_user_api_key(key_id)

View File

@ -37,34 +37,45 @@ class BedrockPing:
@staticmethod
def __slice(in_bytes, pattern):
ret = []
bi = 0 # bytes index
pi = 0 # pattern index
while bi < len(in_bytes):
bytes_index = 0
pattern_index = 0
while bytes_index < len(in_bytes):
try:
f = BedrockPing.fields[pattern[pi]]
field = BedrockPing.fields[pattern[pattern_index]]
except IndexError as index_error:
raise IndexError(
"Ran out of pattern with additional bytes remaining"
) from index_error
if pattern[pi] == "string":
shl = f[0] # string header length
sl = int.from_bytes(
in_bytes[bi : bi + shl], BedrockPing.byte_order, signed=f[1]
) # string length
l = shl + sl
ret.append(in_bytes[bi + shl : bi + shl + sl].decode("ascii"))
elif pattern[pi] == "magic":
l = f[0] # length of field
ret.append(in_bytes[bi : bi + l])
if pattern[pattern_index] == "string":
string_header_length = field[0]
string_length = int.from_bytes(
in_bytes[bytes_index : bytes_index + string_header_length],
BedrockPing.byte_order,
signed=field[1],
)
length = string_header_length + string_length
ret.append(
in_bytes[
bytes_index
+ string_header_length : bytes_index
+ string_header_length
+ string_length
].decode("ascii")
)
elif pattern[pattern_index] == "magic":
length = field[0]
ret.append(in_bytes[bytes_index : bytes_index + length])
else:
l = f[0] # length of field
length = field[0]
ret.append(
int.from_bytes(
in_bytes[bi : bi + l], BedrockPing.byte_order, signed=f[1]
in_bytes[bytes_index : bytes_index + length],
BedrockPing.byte_order,
signed=field[1],
)
)
bi += l
pi += 1
bytes_index += length
pattern_index += 1
return ret
@staticmethod
@ -115,6 +126,6 @@ class BedrockPing:
return self.__recvpong()
except ValueError as e:
print(
f"E: {e}, checking next packet. Retries remaining: {rtr}/{retries}"
f"E: {e}, checking next packet. Retries remaining: {rtr}/{retries}"
)
rtr -= 1

View File

@ -9,7 +9,7 @@ import uuid
import random
from app.classes.minecraft.bedrock_ping import BedrockPing
from app.classes.shared.console import console
from app.classes.shared.console import Console
logger = logging.getLogger(__name__)
@ -78,8 +78,8 @@ class Players(list):
def report(self):
players = []
for x in self:
players.append(str(x))
for player in self:
players.append(str(player))
r_data = {"online": self.online, "max": self.max, "players": players}
@ -106,14 +106,14 @@ def get_code_format(format_name):
return data.get(format_name)
else:
logger.error(f"Format MOTD Error: format name {format_name} does not exist")
console.error(
Console.error(
f"Format MOTD Error: format name {format_name} does not exist"
)
return ""
except Exception as e:
logger.critical(f"Config File Error: Unable to read {format_file} due to {e}")
console.critical(f"Config File Error: Unable to read {format_file} due to {e}")
Console.critical(f"Config File Error: Unable to read {format_file} due to {e}")
return ""
@ -179,11 +179,11 @@ def ping(ip, port):
# For the rest of requests see wiki.vg/Protocol
def ping_bedrock(ip, port):
rd = random.Random()
rand = random.Random()
try:
# pylint: disable=consider-using-f-string
rd.seed("".join(re.findall("..", "%012x" % uuid.getnode())))
client_guid = uuid.UUID(int=rd.getrandbits(32)).int
rand.seed("".join(re.findall("..", "%012x" % uuid.getnode())))
client_guid = uuid.UUID(int=rand.getrandbits(32)).int
except:
client_guid = 0
try:

View File

@ -9,25 +9,25 @@ class ServerProps:
def _parse(self):
# Loads and parses the file specified in self.filepath
with open(self.filepath, encoding="utf-8") as fp:
line = fp.readline()
d = {}
with open(self.filepath, encoding="utf-8") as full_path:
line = full_path.readline()
dictionary = {}
if os.path.exists(".header"):
os.remove(".header")
while line:
if "#" != line[0]:
s = line
s1 = s[: s.find("=")]
if "\n" in s:
s2 = s[s.find("=") + 1 : s.find("\n")]
string = line
string1 = string[: string.find("=")]
if "\n" in string:
string2 = string[string.find("=") + 1 : string.find("\n")]
else:
s2 = s[s.find("=") + 1 :]
d[s1] = s2
string2 = string[string.find("=") + 1 :]
dictionary[string1] = string2
else:
with open(".header", "a+", encoding="utf-8") as h:
h.write(line)
line = fp.readline()
return d
with open(".header", "a+", encoding="utf-8") as header:
header.write(line)
line = full_path.readline()
return dictionary
def print(self):
# Prints the properties dictionary (using pprint)

View File

@ -4,39 +4,33 @@ import time
import shutil
import logging
from datetime import datetime
import requests
from app.classes.controllers.servers_controller import Servers_Controller
from app.classes.models.server_permissions import server_permissions
from app.classes.shared.helpers import helper
from app.classes.web.websocket_helper import websocket_helper
from app.classes.controllers.servers_controller import ServersController
from app.classes.models.server_permissions import PermissionsServers
logger = logging.getLogger(__name__)
try:
import requests
except ModuleNotFoundError as err:
helper.auto_installer_fix(err)
class ServerJars:
def __init__(self):
def __init__(self, helper):
self.helper = helper
self.base_url = "https://serverjars.com"
def _get_api_result(self, call_url: str):
full_url = f"{self.base_url}{call_url}"
try:
r = requests.get(full_url, timeout=2)
response = requests.get(full_url, timeout=2)
if r.status_code not in [200, 201]:
if response.status_code not in [200, 201]:
return {}
except Exception as e:
logger.error(f"Unable to connect to serverjar.com api due to error: {e}")
return {}
try:
api_data = json.loads(r.content)
api_data = json.loads(response.content)
except Exception as e:
logger.error(f"Unable to parse serverjar.com api result due to error: {e}")
return {}
@ -50,9 +44,8 @@ class ServerJars:
return api_response
@staticmethod
def _read_cache():
cache_file = helper.serverjar_cache
def _read_cache(self):
cache_file = self.helper.serverjar_cache
cache = {}
try:
with open(cache_file, "r", encoding="utf-8") as f:
@ -67,37 +60,14 @@ class ServerJars:
data = self._read_cache()
return data.get("servers")
def get_serverjar_data_sorted(self):
data = self.get_serverjar_data()
def str_to_int(x, counter=0):
try:
return ord(x[0]) + str_to_int(x[1:], counter + 1) + len(x)
except IndexError:
return 0
def to_int(x):
try:
return int(x)
except ValueError:
temp = x.split("-")
return to_int(temp[0]) + str_to_int(temp[1]) / 100000
sort_key_fn = lambda x: [to_int(y) for y in x.split(".")]
for key in data.keys():
data[key] = sorted(data[key], key=sort_key_fn)
return data
def _check_api_alive(self):
logger.info("Checking serverjars.com API status")
check_url = f"{self.base_url}/api/fetchTypes"
try:
r = requests.get(check_url, timeout=2)
response = requests.get(check_url, timeout=2)
if r.status_code in [200, 201]:
if response.status_code in [200, 201]:
logger.info("Serverjars.com API is alive")
return True
except Exception as e:
@ -109,8 +79,8 @@ class ServerJars:
def refresh_cache(self):
cache_file = helper.serverjar_cache
cache_old = helper.is_file_older_than_x_days(cache_file)
cache_file = self.helper.serverjar_cache
cache_old = self.helper.is_file_older_than_x_days(cache_file)
# debug override
# cache_old = True
@ -166,6 +136,7 @@ class ServerJars:
def download_jar(self, server, version, path, server_id):
update_thread = threading.Thread(
name=f"server_download-{server_id}-{server}-{version}",
target=self.a_download_jar,
daemon=True,
args=(server, version, path, server_id),
@ -176,46 +147,49 @@ class ServerJars:
# delaying download for server register to finish
time.sleep(3)
fetch_url = f"{self.base_url}/api/fetchJar/{server}/{version}"
server_users = server_permissions.get_server_user_list(server_id)
server_users = PermissionsServers.get_server_user_list(server_id)
# We need to make sure the server is registered before
# we submit a db update for it's stats.
while True:
try:
Servers_Controller.set_download(server_id)
ServersController.set_download(server_id)
for user in server_users:
websocket_helper.broadcast_user(user, "send_start_reload", {})
self.helper.websocket_helper.broadcast_user(
user, "send_start_reload", {}
)
break
except:
logger.debug("server not registered yet. Delaying download.")
except Exception as ex:
logger.debug(f"server not registered yet. Delaying download - {ex}")
# open a file stream
with requests.get(fetch_url, timeout=2, stream=True) as r:
try:
with open(path, "wb") as output:
shutil.copyfileobj(r.raw, output)
Servers_Controller.finish_download(server_id)
ServersController.finish_download(server_id)
for user in server_users:
websocket_helper.broadcast_user(
self.helper.websocket_helper.broadcast_user(
user, "notification", "Executable download finished"
)
time.sleep(3)
websocket_helper.broadcast_user(user, "send_start_reload", {})
self.helper.websocket_helper.broadcast_user(
user, "send_start_reload", {}
)
return True
except Exception as e:
logger.error(f"Unable to save jar to {path} due to error:{e}")
Servers_Controller.finish_download(server_id)
server_users = server_permissions.get_server_user_list(server_id)
ServersController.finish_download(server_id)
server_users = PermissionsServers.get_server_user_list(server_id)
for user in server_users:
websocket_helper.broadcast_user(
self.helper.websocket_helper.broadcast_user(
user, "notification", "Executable download finished"
)
time.sleep(3)
websocket_helper.broadcast_user(user, "send_start_reload", {})
self.helper.websocket_helper.broadcast_user(
user, "send_start_reload", {}
)
return False
server_jar_obj = ServerJars()

View File

@ -5,15 +5,16 @@ import base64
import psutil
from app.classes.minecraft.mc_ping import ping
from app.classes.models.management import Host_Stats
from app.classes.models.servers import servers_helper
from app.classes.shared.helpers import helper
from app.classes.models.management import HostStats
from app.classes.models.servers import HelperServers
from app.classes.shared.helpers import Helpers
logger = logging.getLogger(__name__)
class Stats:
def __init__(self, controller):
def __init__(self, helper, controller):
self.helper = helper
self.controller = controller
def get_node_stats(self):
@ -30,8 +31,8 @@ class Stats:
"cpu_cur_freq": round(cpu_freq[0], 2),
"cpu_max_freq": cpu_freq[2],
"mem_percent": psutil.virtual_memory()[2],
"mem_usage": helper.human_readable_file_size(psutil.virtual_memory()[3]),
"mem_total": helper.human_readable_file_size(psutil.virtual_memory()[0]),
"mem_usage": Helpers.human_readable_file_size(psutil.virtual_memory()[3]),
"mem_total": Helpers.human_readable_file_size(psutil.virtual_memory()[0]),
"disk_data": self._all_disk_usage(),
}
# server_stats = self.get_servers_stats()
@ -60,7 +61,9 @@ class Stats:
with p.oneshot():
process_stats = {
"cpu_usage": real_cpu,
"memory_usage": helper.human_readable_file_size(p.memory_info()[0]),
"memory_usage": Helpers.human_readable_file_size(
p.memory_info()[0]
),
"mem_percentage": round(p.memory_percent(), 0),
}
return process_stats
@ -84,7 +87,7 @@ class Stats:
# print(templ % ("Device", "Total", "Used", "Free", "Use ", "Type","Mount"))
for part in psutil.disk_partitions(all=False):
if helper.is_os_windows():
if Helpers.is_os_windows():
if "cdrom" in part.opts or part.fstype == "":
# skip cd-rom drives with no disk in it; they may raise
# ENOENT, pop-up a Windows GUI error for a non-ready
@ -94,9 +97,9 @@ class Stats:
disk_data.append(
{
"device": part.device,
"total": helper.human_readable_file_size(usage.total),
"used": helper.human_readable_file_size(usage.used),
"free": helper.human_readable_file_size(usage.free),
"total": Helpers.human_readable_file_size(usage.total),
"used": Helpers.human_readable_file_size(usage.used),
"free": Helpers.human_readable_file_size(usage.free),
"percent_used": int(usage.percent),
"fs": part.fstype,
"mount": part.mountpoint,
@ -110,15 +113,15 @@ class Stats:
total_size = 0
total_size = helper.get_dir_size(server_path)
total_size = Helpers.get_dir_size(server_path)
level_total_size = helper.human_readable_file_size(total_size)
level_total_size = Helpers.human_readable_file_size(total_size)
return level_total_size
def get_server_players(self, server_id):
server = servers_helper.get_server_data_by_id(server_id)
server = HelperServers.get_server_data_by_id(server_id)
logger.info(f"Getting players for server {server}")
@ -130,8 +133,8 @@ class Stats:
internal_ip = server["server_ip"]
server_port = server["server_port"]
logger.debug("Pinging {internal_ip} on port {server_port}")
if servers_helper.get_server_type_by_id(server_id) != "minecraft-bedrock":
logger.debug(f"Pinging {internal_ip} on port {server_port}")
if HelperServers.get_server_type_by_id(server_id) != "minecraft-bedrock":
int_mc_ping = ping(internal_ip, int(server_port))
ping_data = {}
@ -171,7 +174,7 @@ class Stats:
return ping_data
@staticmethod
def parse_server_RakNet_ping(ping_obj: object):
def parse_server_raknet_ping(ping_obj: object):
try:
server_icon = base64.encodebytes(ping_obj["icon"])
@ -193,50 +196,23 @@ class Stats:
stats_to_send = self.get_node_stats()
node_stats = stats_to_send.get("node_stats")
Host_Stats.insert(
HostStats.insert(
{
Host_Stats.boot_time: node_stats.get("boot_time", "Unknown"),
Host_Stats.cpu_usage: round(node_stats.get("cpu_usage", 0), 2),
Host_Stats.cpu_cores: node_stats.get("cpu_count", 0),
Host_Stats.cpu_cur_freq: node_stats.get("cpu_cur_freq", 0),
Host_Stats.cpu_max_freq: node_stats.get("cpu_max_freq", 0),
Host_Stats.mem_usage: node_stats.get("mem_usage", "0 MB"),
Host_Stats.mem_percent: node_stats.get("mem_percent", 0),
Host_Stats.mem_total: node_stats.get("mem_total", "0 MB"),
Host_Stats.disk_json: node_stats.get("disk_data", "{}"),
HostStats.boot_time: node_stats.get("boot_time", "Unknown"),
HostStats.cpu_usage: round(node_stats.get("cpu_usage", 0), 2),
HostStats.cpu_cores: node_stats.get("cpu_count", 0),
HostStats.cpu_cur_freq: node_stats.get("cpu_cur_freq", 0),
HostStats.cpu_max_freq: node_stats.get("cpu_max_freq", 0),
HostStats.mem_usage: node_stats.get("mem_usage", "0 MB"),
HostStats.mem_percent: node_stats.get("mem_percent", 0),
HostStats.mem_total: node_stats.get("mem_total", "0 MB"),
HostStats.disk_json: node_stats.get("disk_data", "{}"),
}
).execute()
# server_stats = stats_to_send.get("servers")
# for server in server_stats:
# Server_Stats.insert(
# {
# Server_Stats.server_id: server.get("id", 0),
# Server_Stats.started: server.get("started", ""),
# Server_Stats.running: server.get("running", False),
# Server_Stats.cpu: server.get("cpu", 0),
# Server_Stats.mem: server.get("mem", 0),
# Server_Stats.mem_percent: server.get("mem_percent", 0),
# Server_Stats.world_name: server.get("world_name", ""),
# Server_Stats.world_size: server.get("world_size", ""),
# Server_Stats.server_port: server.get("server_port", ""),
# Server_Stats.int_ping_results: server.get(
# "int_ping_results", False
# ),
# Server_Stats.online: server.get("online", False),
# Server_Stats.max: server.get("max", False),
# Server_Stats.players: server.get("players", False),
# Server_Stats.desc: server.get("desc", False),
# Server_Stats.version: server.get("version", False),
# }
# ).execute()
# delete old data
max_age = helper.get_setting("history_max_age")
max_age = self.helper.get_setting("history_max_age")
now = datetime.datetime.now()
last_week = now.day - max_age
Host_Stats.delete().where(Host_Stats.time < last_week).execute()
# Server_Stats.delete().where(Server_Stats.created < last_week).execute()
HostStats.delete().where(HostStats.time < last_week).execute()

View File

@ -0,0 +1,8 @@
import peewee
database_proxy = peewee.DatabaseProxy()
class BaseModel(peewee.Model):
class Meta:
database = database_proxy

View File

@ -1,35 +1,22 @@
import logging
from app.classes.shared.helpers import helper
from app.classes.shared.permission_helper import permission_helper
from app.classes.models.users import Users, ApiKeys
try:
from peewee import (
SqliteDatabase,
Model,
ForeignKeyField,
CharField,
IntegerField,
DoesNotExist,
)
from enum import Enum
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
logger = logging.getLogger(__name__)
peewee_logger = logging.getLogger("peewee")
peewee_logger.setLevel(logging.INFO)
database = SqliteDatabase(
helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
from enum import Enum
from peewee import (
ForeignKeyField,
CharField,
IntegerField,
DoesNotExist,
)
from app.classes.models.base_model import BaseModel
from app.classes.models.users import Users, ApiKeys, HelperUsers
from app.classes.shared.permission_helper import PermissionHelper
logger = logging.getLogger(__name__)
# **********************************************************************************
# User_Crafty Class
# **********************************************************************************
class User_Crafty(Model):
class UserCrafty(BaseModel):
user_id = ForeignKeyField(Users, backref="users_crafty")
permissions = CharField(default="00000000")
limit_server_creation = IntegerField(default=-1)
@ -41,39 +28,38 @@ class User_Crafty(Model):
class Meta:
table_name = "user_crafty"
database = database
# **********************************************************************************
# Crafty Permissions Class
# **********************************************************************************
class Enum_Permissions_Crafty(Enum):
Server_Creation = 0
User_Config = 1
Roles_Config = 2
class EnumPermissionsCrafty(Enum):
SERVER_CREATION = 0
USER_CONFIG = 1
ROLES_CONFIG = 2
class Permissions_Crafty:
class PermissionsCrafty:
# **********************************************************************************
# Crafty Permissions Methods
# **********************************************************************************
@staticmethod
def get_permissions_list():
permissions_list = []
for member in Enum_Permissions_Crafty.__members__.items():
for member in EnumPermissionsCrafty.__members__.items():
permissions_list.append(member[1])
return permissions_list
@staticmethod
def get_permissions(permissions_mask):
permissions_list = []
for member in Enum_Permissions_Crafty.__members__.items():
if crafty_permissions.has_permission(permissions_mask, member[1]):
for member in EnumPermissionsCrafty.__members__.items():
if PermissionsCrafty.has_permission(permissions_mask, member[1]):
permissions_list.append(member[1])
return permissions_list
@staticmethod
def has_permission(permission_mask, permission_tested: Enum_Permissions_Crafty):
def has_permission(permission_mask, permission_tested: EnumPermissionsCrafty):
result = False
if permission_mask[permission_tested.value] == "1":
result = True
@ -81,40 +67,40 @@ class Permissions_Crafty:
@staticmethod
def set_permission(
permission_mask, permission_tested: Enum_Permissions_Crafty, value
permission_mask, permission_tested: EnumPermissionsCrafty, value
):
l = list(permission_mask)
l[permission_tested.value] = str(value)
permission_mask = "".join(l)
lst = list(permission_mask)
lst[permission_tested.value] = str(value)
permission_mask = "".join(lst)
return permission_mask
@staticmethod
def get_permission(permission_mask, permission_tested: Enum_Permissions_Crafty):
def get_permission(permission_mask, permission_tested: EnumPermissionsCrafty):
return permission_mask[permission_tested.value]
@staticmethod
def get_crafty_permissions_mask(user_id):
permissions_mask = ""
user_crafty = crafty_permissions.get_User_Crafty(user_id)
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
permissions_mask = user_crafty.permissions
return permissions_mask
@staticmethod
def get_all_permission_quantity_list():
quantity_list = {
Enum_Permissions_Crafty.Server_Creation.name: -1,
Enum_Permissions_Crafty.User_Config.name: -1,
Enum_Permissions_Crafty.Roles_Config.name: -1,
EnumPermissionsCrafty.SERVER_CREATION.name: -1,
EnumPermissionsCrafty.USER_CONFIG.name: -1,
EnumPermissionsCrafty.ROLES_CONFIG.name: -1,
}
return quantity_list
@staticmethod
def get_permission_quantity_list(user_id):
user_crafty = crafty_permissions.get_User_Crafty(user_id)
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
quantity_list = {
Enum_Permissions_Crafty.Server_Creation.name: user_crafty.limit_server_creation, # pylint: disable=line-too-long
Enum_Permissions_Crafty.User_Config.name: user_crafty.limit_user_creation,
Enum_Permissions_Crafty.Roles_Config.name: user_crafty.limit_role_creation,
EnumPermissionsCrafty.SERVER_CREATION.name: user_crafty.limit_server_creation, # pylint: disable=line-too-long
EnumPermissionsCrafty.USER_CONFIG.name: user_crafty.limit_user_creation,
EnumPermissionsCrafty.ROLES_CONFIG.name: user_crafty.limit_role_creation,
}
return quantity_list
@ -122,31 +108,29 @@ class Permissions_Crafty:
# User_Crafty Methods
# **********************************************************************************
@staticmethod
def get_User_Crafty(user_id):
def get_user_crafty(user_id):
try:
user_crafty = (
User_Crafty.select().where(User_Crafty.user_id == user_id).get()
)
user_crafty = UserCrafty.select().where(UserCrafty.user_id == user_id).get()
except DoesNotExist:
user_crafty = User_Crafty.insert(
user_crafty = UserCrafty.insert(
{
User_Crafty.user_id: user_id,
User_Crafty.permissions: "000",
User_Crafty.limit_server_creation: 0,
User_Crafty.limit_user_creation: 0,
User_Crafty.limit_role_creation: 0,
User_Crafty.created_server: 0,
User_Crafty.created_user: 0,
User_Crafty.created_role: 0,
UserCrafty.user_id: user_id,
UserCrafty.permissions: "000",
UserCrafty.limit_server_creation: 0,
UserCrafty.limit_user_creation: 0,
UserCrafty.limit_role_creation: 0,
UserCrafty.created_server: 0,
UserCrafty.created_user: 0,
UserCrafty.created_role: 0,
}
).execute()
user_crafty = crafty_permissions.get_User_Crafty(user_id)
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
return user_crafty
@staticmethod
def add_user_crafty(user_id, uc_permissions):
user_crafty = User_Crafty.insert(
{User_Crafty.user_id: user_id, User_Crafty.permissions: uc_permissions}
user_crafty = UserCrafty.insert(
{UserCrafty.user_id: user_id, UserCrafty.permissions: uc_permissions}
).execute()
return user_crafty
@ -159,46 +143,44 @@ class Permissions_Crafty:
limit_role_creation,
):
try:
user_crafty = (
User_Crafty.select().where(User_Crafty.user_id == user_id).get()
)
user_crafty = UserCrafty.select().where(UserCrafty.user_id == user_id).get()
user_crafty.permissions = permissions_mask
user_crafty.limit_server_creation = limit_server_creation
user_crafty.limit_user_creation = limit_user_creation
user_crafty.limit_role_creation = limit_role_creation
User_Crafty.save(user_crafty)
UserCrafty.save(user_crafty)
except:
User_Crafty.insert(
UserCrafty.insert(
{
User_Crafty.user_id: user_id,
User_Crafty.permissions: permissions_mask,
User_Crafty.limit_server_creation: limit_server_creation,
User_Crafty.limit_user_creation: limit_user_creation,
User_Crafty.limit_role_creation: limit_role_creation,
UserCrafty.user_id: user_id,
UserCrafty.permissions: permissions_mask,
UserCrafty.limit_server_creation: limit_server_creation,
UserCrafty.limit_user_creation: limit_user_creation,
UserCrafty.limit_role_creation: limit_role_creation,
}
).execute()
@staticmethod
def get_created_quantity_list(user_id):
user_crafty = crafty_permissions.get_User_Crafty(user_id)
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
quantity_list = {
Enum_Permissions_Crafty.Server_Creation.name: user_crafty.created_server,
Enum_Permissions_Crafty.User_Config.name: user_crafty.created_user,
Enum_Permissions_Crafty.Roles_Config.name: user_crafty.created_role,
EnumPermissionsCrafty.SERVER_CREATION.name: user_crafty.created_server,
EnumPermissionsCrafty.USER_CONFIG.name: user_crafty.created_user,
EnumPermissionsCrafty.ROLES_CONFIG.name: user_crafty.created_role,
}
return quantity_list
@staticmethod
def get_crafty_limit_value(user_id, permission):
quantity_list = crafty_permissions.get_permission_quantity_list(user_id)
quantity_list = PermissionsCrafty.get_permission_quantity_list(user_id)
return quantity_list[permission]
@staticmethod
def can_add_in_crafty(user_id, permission):
user_crafty = crafty_permissions.get_User_Crafty(user_id)
can = crafty_permissions.has_permission(user_crafty.permissions, permission)
limit_list = crafty_permissions.get_permission_quantity_list(user_id)
quantity_list = crafty_permissions.get_created_quantity_list(user_id)
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
can = PermissionsCrafty.has_permission(user_crafty.permissions, permission)
limit_list = PermissionsCrafty.get_permission_quantity_list(user_id)
quantity_list = PermissionsCrafty.get_created_quantity_list(user_id)
return can and (
(quantity_list[permission.name] < limit_list[permission.name])
or limit_list[permission.name] == -1
@ -206,26 +188,26 @@ class Permissions_Crafty:
@staticmethod
def add_server_creation(user_id):
user_crafty = crafty_permissions.get_User_Crafty(user_id)
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
user_crafty.created_server += 1
User_Crafty.save(user_crafty)
UserCrafty.save(user_crafty)
return user_crafty.created_server
@staticmethod
def get_api_key_permissions_list(key: ApiKeys):
user = key.user
if user.superuser and key.superuser:
return crafty_permissions.get_permissions_list()
user = HelperUsers.get_user(key.user_id)
if user["superuser"] and key.superuser:
return PermissionsCrafty.get_permissions_list()
else:
user_permissions_mask = crafty_permissions.get_crafty_permissions_mask(
user.user_id
)
if user["superuser"]:
user_permissions_mask = "111"
else:
user_permissions_mask = PermissionsCrafty.get_crafty_permissions_mask(
user["user_id"]
)
key_permissions_mask: str = key.crafty_permissions
permissions_mask = permission_helper.combine_masks(
permissions_mask = PermissionHelper.combine_masks(
user_permissions_mask, key_permissions_mask
)
permissions_list = crafty_permissions.get_permissions(permissions_mask)
permissions_list = PermissionsCrafty.get_permissions(permissions_mask)
return permissions_list
crafty_permissions = Permissions_Crafty()

View File

@ -1,42 +1,29 @@
import logging
import datetime
from peewee import (
ForeignKeyField,
CharField,
IntegerField,
DateTimeField,
FloatField,
TextField,
AutoField,
BooleanField,
)
from playhouse.shortcuts import model_to_dict
from app.classes.models.users import Users, users_helper
from app.classes.models.base_model import BaseModel
from app.classes.models.users import Users, HelperUsers
from app.classes.models.servers import Servers
from app.classes.models.server_permissions import server_permissions
from app.classes.shared.helpers import helper
from app.classes.shared.main_models import db_helper
from app.classes.web.websocket_helper import websocket_helper
try:
from peewee import (
SqliteDatabase,
Model,
ForeignKeyField,
CharField,
IntegerField,
DateTimeField,
FloatField,
TextField,
AutoField,
BooleanField,
)
from playhouse.shortcuts import model_to_dict
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
from app.classes.models.server_permissions import PermissionsServers
from app.classes.shared.main_models import DatabaseShortcuts
logger = logging.getLogger(__name__)
peewee_logger = logging.getLogger("peewee")
peewee_logger.setLevel(logging.INFO)
database = SqliteDatabase(
helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
)
# **********************************************************************************
# Audit_Log Class
# **********************************************************************************
class Audit_Log(Model):
class AuditLog(BaseModel):
audit_id = AutoField()
created = DateTimeField(default=datetime.datetime.now)
user_name = CharField(default="")
@ -48,13 +35,13 @@ class Audit_Log(Model):
log_msg = TextField(default="")
class Meta:
database = database
table_name = "audit_log"
# **********************************************************************************
# Host_Stats Class
# **********************************************************************************
class Host_Stats(Model):
class HostStats(BaseModel):
time = DateTimeField(default=datetime.datetime.now, index=True)
boot_time = CharField(default="")
cpu_usage = FloatField(default=0)
@ -68,13 +55,12 @@ class Host_Stats(Model):
class Meta:
table_name = "host_stats"
database = database
# **********************************************************************************
# Commands Class
# **********************************************************************************
class Commands(Model):
class Commands(BaseModel):
command_id = AutoField()
created = DateTimeField(default=datetime.datetime.now)
server_id = ForeignKeyField(Servers, backref="server", index=True)
@ -85,13 +71,12 @@ class Commands(Model):
class Meta:
table_name = "commands"
database = database
# **********************************************************************************
# Webhooks Class
# **********************************************************************************
class Webhooks(Model):
class Webhooks(BaseModel):
id = AutoField()
name = CharField(max_length=64, unique=True, index=True)
method = CharField(default="POST")
@ -101,13 +86,12 @@ class Webhooks(Model):
class Meta:
table_name = "webhooks"
database = database
# **********************************************************************************
# Schedules Class
# **********************************************************************************
class Schedules(Model):
class Schedules(BaseModel):
schedule_id = IntegerField(unique=True, primary_key=True)
server_id = ForeignKeyField(Servers, backref="schedule_server")
enabled = BooleanField()
@ -124,13 +108,12 @@ class Schedules(Model):
class Meta:
table_name = "schedules"
database = database
# **********************************************************************************
# Backups Class
# **********************************************************************************
class Backups(Model):
class Backups(BaseModel):
excluded_dirs = CharField(null=True)
max_backups = IntegerField()
server_id = ForeignKeyField(Servers, backref="backups_server")
@ -138,10 +121,12 @@ class Backups(Model):
class Meta:
table_name = "backups"
database = database
class helpers_management:
class HelpersManagement:
def __init__(self, database, helper):
self.database = database
self.helper = helper
# **********************************************************************************
# Host_Stats Methods
@ -149,7 +134,7 @@ class helpers_management:
@staticmethod
def get_latest_hosts_stats():
# pylint: disable=no-member
query = Host_Stats.select().order_by(Host_Stats.id.desc()).get()
query = HostStats.select().order_by(HostStats.id.desc()).get()
return model_to_dict(query)
# **********************************************************************************
@ -184,62 +169,60 @@ class helpers_management:
# **********************************************************************************
@staticmethod
def get_actity_log():
q = Audit_Log.select()
return db_helper.return_db_rows(q)
query = AuditLog.select()
return DatabaseShortcuts.return_db_rows(query)
@staticmethod
def add_to_audit_log(user_id, log_msg, server_id=None, source_ip=None):
def add_to_audit_log(self, user_id, log_msg, server_id=None, source_ip=None):
logger.debug(f"Adding to audit log User:{user_id} - Message: {log_msg} ")
user_data = users_helper.get_user(user_id)
user_data = HelperUsers.get_user(user_id)
audit_msg = f"{str(user_data['username']).capitalize()} {log_msg}"
server_users = server_permissions.get_server_user_list(server_id)
server_users = PermissionsServers.get_server_user_list(server_id)
for user in server_users:
websocket_helper.broadcast_user(user, "notification", audit_msg)
self.helper.websocket_helper.broadcast_user(user, "notification", audit_msg)
Audit_Log.insert(
AuditLog.insert(
{
Audit_Log.user_name: user_data["username"],
Audit_Log.user_id: user_id,
Audit_Log.server_id: server_id,
Audit_Log.log_msg: audit_msg,
Audit_Log.source_ip: source_ip,
AuditLog.user_name: user_data["username"],
AuditLog.user_id: user_id,
AuditLog.server_id: server_id,
AuditLog.log_msg: audit_msg,
AuditLog.source_ip: source_ip,
}
).execute()
# deletes records when they're more than 100
ordered = Audit_Log.select().order_by(+Audit_Log.created)
ordered = AuditLog.select().order_by(+AuditLog.created)
for item in ordered:
if not helper.get_setting("max_audit_entries"):
if not self.helper.get_setting("max_audit_entries"):
max_entries = 300
else:
max_entries = helper.get_setting("max_audit_entries")
if Audit_Log.select().count() > max_entries:
Audit_Log.delete().where(Audit_Log.audit_id == item.audit_id).execute()
max_entries = self.helper.get_setting("max_audit_entries")
if AuditLog.select().count() > max_entries:
AuditLog.delete().where(AuditLog.audit_id == item.audit_id).execute()
else:
return
@staticmethod
def add_to_audit_log_raw(user_name, user_id, server_id, log_msg, source_ip):
Audit_Log.insert(
def add_to_audit_log_raw(self, user_name, user_id, server_id, log_msg, source_ip):
AuditLog.insert(
{
Audit_Log.user_name: user_name,
Audit_Log.user_id: user_id,
Audit_Log.server_id: server_id,
Audit_Log.log_msg: log_msg,
Audit_Log.source_ip: source_ip,
AuditLog.user_name: user_name,
AuditLog.user_id: user_id,
AuditLog.server_id: server_id,
AuditLog.log_msg: log_msg,
AuditLog.source_ip: source_ip,
}
).execute()
# deletes records when they're more than 100
ordered = Audit_Log.select().order_by(+Audit_Log.created)
ordered = AuditLog.select().order_by(+AuditLog.created)
for item in ordered:
# configurable through app/config/config.json
if not helper.get_setting("max_audit_entries"):
if not self.helper.get_setting("max_audit_entries"):
max_entries = 300
else:
max_entries = helper.get_setting("max_audit_entries")
if Audit_Log.select().count() > max_entries:
Audit_Log.delete().where(Audit_Log.audit_id == item.audit_id).execute()
max_entries = self.helper.get_setting("max_audit_entries")
if AuditLog.select().count() > max_entries:
AuditLog.delete().where(AuditLog.audit_id == item.audit_id).execute()
else:
return
@ -322,8 +305,11 @@ class helpers_management:
@staticmethod
def get_schedules_enabled():
# pylint: disable=singleton-comparison
return Schedules.select().where(Schedules.enabled == True).execute()
return (
Schedules.select()
.where(Schedules.enabled == True) # pylint: disable=singleton-comparison
.execute()
)
# **********************************************************************************
# Backups Methods
@ -351,8 +337,8 @@ class helpers_management:
}
return conf
@staticmethod
def set_backup_config(
self,
server_id: int,
backup_path: str = None,
max_backups: int = None,
@ -378,21 +364,24 @@ class helpers_management:
conf["excluded_dirs"] = dirs_to_exclude
conf["compress"] = compress
if not new_row:
with database.atomic():
with self.database.atomic():
if backup_path is not None:
u1 = (
server_rows = (
Servers.update(backup_path=backup_path)
.where(Servers.server_id == server_id)
.execute()
)
else:
u1 = 0
u2 = (
server_rows = 0
backup_rows = (
Backups.update(conf).where(Backups.server_id == server_id).execute()
)
logger.debug(f"Updating existing backup record. {u1}+{u2} rows affected")
logger.debug(
f"Updating existing backup record. "
f"{server_rows}+{backup_rows} rows affected"
)
else:
with database.atomic():
with self.database.atomic():
conf["server_id"] = server_id
if backup_path is not None:
Servers.update(backup_path=backup_path).where(
@ -401,8 +390,9 @@ class helpers_management:
Backups.create(**conf)
logger.debug("Creating new backup record.")
def get_excluded_backup_dirs(self, server_id: int):
excluded_dirs = self.get_backup_config(server_id)["excluded_dirs"]
@staticmethod
def get_excluded_backup_dirs(server_id: int):
excluded_dirs = HelpersManagement.get_backup_config(server_id)["excluded_dirs"]
if excluded_dirs is not None and excluded_dirs != "":
dir_list = excluded_dirs.split(",")
else:
@ -435,12 +425,6 @@ class helpers_management:
@staticmethod
def clear_unexecuted_commands():
Commands.update(
{
Commands.executed: True
# pylint: disable=singleton-comparison
}
).where(Commands.executed == False).execute()
management_helper = helpers_management()
Commands.update({Commands.executed: True}).where(
Commands.executed == False # pylint: disable=singleton-comparison
).execute()

View File

@ -1,33 +1,22 @@
import logging
import datetime
from peewee import (
CharField,
DoesNotExist,
AutoField,
DateTimeField,
)
from playhouse.shortcuts import model_to_dict
from app.classes.shared.helpers import helper
try:
from peewee import (
SqliteDatabase,
Model,
CharField,
DoesNotExist,
AutoField,
DateTimeField,
)
from playhouse.shortcuts import model_to_dict
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
from app.classes.models.base_model import BaseModel
from app.classes.shared.helpers import Helpers
logger = logging.getLogger(__name__)
peewee_logger = logging.getLogger("peewee")
peewee_logger.setLevel(logging.INFO)
database = SqliteDatabase(
helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
)
# **********************************************************************************
# Roles Class
# **********************************************************************************
class Roles(Model):
class Roles(BaseModel):
role_id = AutoField()
created = DateTimeField(default=datetime.datetime.now)
last_update = DateTimeField(default=datetime.datetime.now)
@ -35,13 +24,15 @@ class Roles(Model):
class Meta:
table_name = "roles"
database = database
# **********************************************************************************
# Roles Helpers
# **********************************************************************************
class helper_roles:
class HelperRoles:
def __init__(self, database):
self.database = database
@staticmethod
def get_all_roles():
query = Roles.select()
@ -63,7 +54,7 @@ class helper_roles:
role_id = Roles.insert(
{
Roles.role_name: role_name.lower(),
Roles.created: helper.get_time_as_string(),
Roles.created: Helpers.get_time_as_string(),
}
).execute()
return role_id
@ -72,17 +63,13 @@ class helper_roles:
def update_role(role_id, up_data):
return Roles.update(up_data).where(Roles.role_id == role_id).execute()
@staticmethod
def remove_role(role_id):
with database.atomic():
def remove_role(self, role_id):
with self.database.atomic():
role = Roles.get(Roles.role_id == role_id)
return role.delete_instance()
@staticmethod
def role_id_exists(role_id):
if not roles_helper.get_role(role_id):
if not HelperRoles.get_role(role_id):
return False
return True
roles_helper = helper_roles()

View File

@ -1,36 +1,24 @@
from enum import Enum
import logging
from peewee import (
ForeignKeyField,
CharField,
CompositeKey,
JOIN,
)
from app.classes.models.base_model import BaseModel
from app.classes.models.servers import Servers
from app.classes.models.roles import Roles
from app.classes.models.users import User_Roles, users_helper, ApiKeys, Users
from app.classes.shared.helpers import helper
from app.classes.shared.permission_helper import permission_helper
try:
from peewee import (
SqliteDatabase,
Model,
ForeignKeyField,
CharField,
CompositeKey,
JOIN,
)
from enum import Enum
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
from app.classes.models.users import UserRoles, HelperUsers, ApiKeys, Users
from app.classes.shared.permission_helper import PermissionHelper
logger = logging.getLogger(__name__)
peewee_logger = logging.getLogger("peewee")
peewee_logger.setLevel(logging.INFO)
database = SqliteDatabase(
helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
)
# **********************************************************************************
# Role Servers Class
# **********************************************************************************
class Role_Servers(Model):
class RoleServers(BaseModel):
role_id = ForeignKeyField(Roles, backref="role_server")
server_id = ForeignKeyField(Servers, backref="role_server")
permissions = CharField(default="00000000")
@ -38,52 +26,51 @@ class Role_Servers(Model):
class Meta:
table_name = "role_servers"
primary_key = CompositeKey("role_id", "server_id")
database = database
# **********************************************************************************
# Servers Permissions Class
# **********************************************************************************
class Enum_Permissions_Server(Enum):
Commands = 0
Terminal = 1
Logs = 2
Schedule = 3
Backup = 4
Files = 5
Config = 6
Players = 7
class EnumPermissionsServer(Enum):
COMMANDS = 0
TERMINAL = 1
LOGS = 2
SCHEDULE = 3
BACKUP = 4
FILES = 5
CONFIG = 6
PLAYERS = 7
class Permissions_Servers:
class PermissionsServers:
@staticmethod
def get_or_create(role_id, server, permissions_mask):
return Role_Servers.get_or_create(
return RoleServers.get_or_create(
role_id=role_id, server_id=server, permissions=permissions_mask
)
@staticmethod
def get_permissions_list():
permissions_list = []
for member in Enum_Permissions_Server.__members__.items():
for member in EnumPermissionsServer.__members__.items():
permissions_list.append(member[1])
return permissions_list
@staticmethod
def get_permissions(permissions_mask):
permissions_list = []
for member in Enum_Permissions_Server.__members__.items():
if server_permissions.has_permission(permissions_mask, member[1]):
for member in EnumPermissionsServer.__members__.items():
if PermissionsServers.has_permission(permissions_mask, member[1]):
permissions_list.append(member[1])
return permissions_list
@staticmethod
def has_permission(permission_mask, permission_tested: Enum_Permissions_Server):
def has_permission(permission_mask, permission_tested: EnumPermissionsServer):
return permission_mask[permission_tested.value] == "1"
@staticmethod
def set_permission(
permission_mask, permission_tested: Enum_Permissions_Server, value
permission_mask, permission_tested: EnumPermissionsServer, value
):
list_perms = list(permission_mask)
list_perms[permission_tested.value] = str(value)
@ -91,14 +78,14 @@ class Permissions_Servers:
return permission_mask
@staticmethod
def get_permission(permission_mask, permission_tested: Enum_Permissions_Server):
def get_permission(permission_mask, permission_tested: EnumPermissionsServer):
return permission_mask[permission_tested.value]
@staticmethod
def get_token_permissions(permissions_mask, api_permissions_mask):
permissions_list = []
for member in Enum_Permissions_Server.__members__.items():
if permission_helper.both_have_perm(
for member in EnumPermissionsServer.__members__.items():
if PermissionHelper.both_have_perm(
permissions_mask, api_permissions_mask, member[1]
):
permissions_list.append(member[1])
@ -109,31 +96,31 @@ class Permissions_Servers:
# **********************************************************************************
@staticmethod
def get_role_servers_from_role_id(roleid):
return Role_Servers.select().where(Role_Servers.role_id == roleid)
return RoleServers.select().where(RoleServers.role_id == roleid)
@staticmethod
def get_servers_from_role(role_id):
return (
Role_Servers.select()
RoleServers.select()
.join(Servers, JOIN.INNER)
.where(Role_Servers.role_id == role_id)
.where(RoleServers.role_id == role_id)
)
@staticmethod
def get_roles_from_server(server_id):
return (
Role_Servers.select()
RoleServers.select()
.join(Roles, JOIN.INNER)
.where(Role_Servers.server_id == server_id)
.where(RoleServers.server_id == server_id)
)
@staticmethod
def add_role_server(server_id, role_id, rs_permissions="00000000"):
servers = Role_Servers.insert(
servers = RoleServers.insert(
{
Role_Servers.server_id: server_id,
Role_Servers.role_id: role_id,
Role_Servers.permissions: rs_permissions,
RoleServers.server_id: server_id,
RoleServers.role_id: role_id,
RoleServers.permissions: rs_permissions,
}
).execute()
return servers
@ -142,9 +129,9 @@ class Permissions_Servers:
def get_permissions_mask(role_id, server_id):
permissions_mask = ""
role_server = (
Role_Servers.select()
.where(Role_Servers.role_id == role_id)
.where(Role_Servers.server_id == server_id)
RoleServers.select()
.where(RoleServers.role_id == role_id)
.where(RoleServers.server_id == server_id)
.get()
)
permissions_mask = role_server.permissions
@ -153,9 +140,7 @@ class Permissions_Servers:
@staticmethod
def get_server_roles(server_id):
role_list = []
roles = (
Role_Servers.select().where(Role_Servers.server_id == server_id).execute()
)
roles = RoleServers.select().where(RoleServers.server_id == server_id).execute()
for role in roles:
role_list.append(role.role_id)
return role_list
@ -163,74 +148,70 @@ class Permissions_Servers:
@staticmethod
def get_role_permissions_list(role_id):
permissions_mask = "00000000"
role_server = Role_Servers.get_or_none(Role_Servers.role_id == role_id)
role_server = RoleServers.get_or_none(RoleServers.role_id == role_id)
if role_server is not None:
permissions_mask = role_server.permissions
permissions_list = server_permissions.get_permissions(permissions_mask)
permissions_list = PermissionsServers.get_permissions(permissions_mask)
return permissions_list
@staticmethod
def update_role_permission(role_id, server_id, permissions_mask):
role_server = (
Role_Servers.select()
.where(Role_Servers.role_id == role_id)
.where(Role_Servers.server_id == server_id)
RoleServers.select()
.where(RoleServers.role_id == role_id)
.where(RoleServers.server_id == server_id)
.get()
)
role_server.permissions = permissions_mask
Role_Servers.save(role_server)
RoleServers.save(role_server)
@staticmethod
def delete_roles_permissions(role_id, removed_servers=None):
if removed_servers is None:
removed_servers = {}
return (
Role_Servers.delete()
.where(Role_Servers.role_id == role_id)
.where(Role_Servers.server_id.in_(removed_servers))
RoleServers.delete()
.where(RoleServers.role_id == role_id)
.where(RoleServers.server_id.in_(removed_servers))
.execute()
)
@staticmethod
def remove_roles_of_server(server_id):
with database.atomic():
return (
Role_Servers.delete()
.where(Role_Servers.server_id == server_id)
.execute()
)
return RoleServers.delete().where(RoleServers.server_id == server_id).execute()
@staticmethod
def get_user_id_permissions_mask(user_id, server_id: str):
user = users_helper.get_user_model(user_id)
return server_permissions.get_user_permissions_mask(user, server_id)
user = HelperUsers.get_user_model(user_id)
return PermissionsServers.get_user_permissions_mask(user, server_id)
@staticmethod
def get_user_permissions_mask(user: Users, server_id: str):
if user.superuser:
permissions_mask = "1" * len(server_permissions.get_permissions_list())
permissions_mask = "1" * len(PermissionsServers.get_permissions_list())
else:
roles_list = users_helper.get_user_roles_id(user.user_id)
roles_list = HelperUsers.get_user_roles_id(user.user_id)
role_server = (
Role_Servers.select()
.where(Role_Servers.role_id.in_(roles_list))
.where(Role_Servers.server_id == server_id)
RoleServers.select()
.where(RoleServers.role_id.in_(roles_list))
.where(RoleServers.server_id == server_id)
.execute()
)
try:
permissions_mask = role_server[0].permissions
except IndexError:
permissions_mask = "0" * len(server_permissions.get_permissions_list())
permissions_mask = "0" * len(PermissionsServers.get_permissions_list())
return permissions_mask
@staticmethod
def get_server_user_list(server_id):
final_users = []
server_roles = Role_Servers.select().where(Role_Servers.server_id == server_id)
# pylint: disable=singleton-comparison
super_users = Users.select().where(Users.superuser == True)
server_roles = RoleServers.select().where(RoleServers.server_id == server_id)
super_users = Users.select().where(
Users.superuser == True # pylint: disable=singleton-comparison
)
for role in server_roles:
users = User_Roles.select().where(User_Roles.role_id == role.role_id)
users = UserRoles.select().where(UserRoles.role_id == role.role_id)
for user in users:
if user.user_id.user_id not in final_users:
final_users.append(user.user_id.user_id)
@ -241,45 +222,48 @@ class Permissions_Servers:
@staticmethod
def get_user_id_permissions_list(user_id, server_id: str):
user = users_helper.get_user_model(user_id)
return server_permissions.get_user_permissions_list(user, server_id)
user = HelperUsers.get_user_model(user_id)
return PermissionsServers.get_user_permissions_list(user, server_id)
@staticmethod
def get_user_permissions_list(user: Users, server_id: str):
if user.superuser:
permissions_list = server_permissions.get_permissions_list()
permissions_list = PermissionsServers.get_permissions_list()
else:
permissions_mask = server_permissions.get_user_permissions_mask(
permissions_mask = PermissionsServers.get_user_permissions_mask(
user, server_id
)
permissions_list = server_permissions.get_permissions(permissions_mask)
permissions_list = PermissionsServers.get_permissions(permissions_mask)
return permissions_list
@staticmethod
def get_api_key_id_permissions_list(key_id, server_id: str):
key = ApiKeys.get(ApiKeys.token_id == key_id)
return server_permissions.get_api_key_permissions_list(key, server_id)
return PermissionsServers.get_api_key_permissions_list(key, server_id)
@staticmethod
def get_api_key_permissions_list(key: ApiKeys, server_id: str):
user = key.user
if user.superuser and key.superuser:
return server_permissions.get_permissions_list()
user = HelperUsers.get_user(key.user_id)
if user["superuser"] and key.superuser:
return PermissionsServers.get_permissions_list()
else:
roles_list = users_helper.get_user_roles_id(user["user_id"])
roles_list = HelperUsers.get_user_roles_id(user["user_id"])
role_server = (
Role_Servers.select()
.where(Role_Servers.role_id.in_(roles_list))
.where(Role_Servers.server_id == server_id)
RoleServers.select()
.where(RoleServers.role_id.in_(roles_list))
.where(RoleServers.server_id == server_id)
.execute()
)
user_permissions_mask = role_server[0].permissions
try:
user_permissions_mask = role_server[0].permissions
except:
if user["superuser"]:
user_permissions_mask = "11111111"
else:
user_permissions_mask = "00000000"
key_permissions_mask = key.server_permissions
permissions_mask = permission_helper.combine_masks(
permissions_mask = PermissionHelper.combine_masks(
user_permissions_mask, key_permissions_mask
)
permissions_list = server_permissions.get_permissions(permissions_mask)
permissions_list = PermissionsServers.get_permissions(permissions_mask)
return permissions_list
server_permissions = Permissions_Servers()

View File

@ -0,0 +1,355 @@
import os
import logging
import datetime
from app.classes.models.servers import Servers, HelperServers
from app.classes.shared.helpers import Helpers
from app.classes.shared.main_models import DatabaseShortcuts
from app.classes.shared.migration import MigrationManager
try:
from peewee import (
SqliteDatabase,
Model,
DatabaseProxy,
ForeignKeyField,
CharField,
AutoField,
DateTimeField,
BooleanField,
IntegerField,
FloatField,
)
except ModuleNotFoundError as e:
Helpers.auto_installer_fix(e)
logger = logging.getLogger(__name__)
peewee_logger = logging.getLogger("peewee")
peewee_logger.setLevel(logging.INFO)
database_stats_proxy = DatabaseProxy()
# **********************************************************************************
# Servers Stats Class
# **********************************************************************************
class ServerStats(Model):
stats_id = AutoField()
created = DateTimeField(default=datetime.datetime.now)
server_id = ForeignKeyField(Servers, backref="server", index=True)
started = CharField(default="")
running = BooleanField(default=False)
cpu = FloatField(default=0)
mem = FloatField(default=0)
mem_percent = FloatField(default=0)
world_name = CharField(default="")
world_size = CharField(default="")
server_port = IntegerField(default=25565)
int_ping_results = CharField(default="")
online = IntegerField(default=0)
max = IntegerField(default=0)
players = CharField(default="")
desc = CharField(default="Unable to Connect")
version = CharField(default="")
updating = BooleanField(default=False)
waiting_start = BooleanField(default=False)
first_run = BooleanField(default=True)
crashed = BooleanField(default=False)
downloading = BooleanField(default=False)
class Meta:
table_name = "server_stats"
database = database_stats_proxy
# **********************************************************************************
# Servers_Stats Methods
# **********************************************************************************
class HelperServerStats:
def __init__(self, database):
self.database = database
@staticmethod
def init_database(server_id):
try:
server = HelperServers.get_server_data_by_id(server_id)
db_folder = os.path.join(f"{server['path']}", "db_stats")
db_file = os.path.join(
db_folder,
f"{server['server_name']}" + ".sqlite",
)
database = SqliteDatabase(
db_file, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
)
if not os.path.exists(db_file):
try:
os.mkdir(db_folder)
except Exception as ex:
logger.warning(
f"Error try to create the db_stats folder for server : {ex}"
)
helper_stats = Helpers()
helper_stats.migration_dir = os.path.join(
f"{helper_stats.migration_dir}", "stats"
)
helper_stats.db_path = db_file
database_stats_proxy.initialize(database)
migration_manager = MigrationManager(database, helper_stats)
migration_manager.up() # Automatically runs migrations
database_stats_proxy.initialize(database)
except Exception as ex:
logger.warning(
f"Error try to look for the db_stats files for server : {ex}"
)
@staticmethod
def select_database(server_id):
try:
server = HelperServers.get_server_data_by_id(server_id)
db_file = os.path.join(
f"{server['path']}",
"db_stats",
f"{server['server_name']}" + ".sqlite",
)
database = SqliteDatabase(
db_file, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
)
database_stats_proxy.initialize(database)
except Exception as ex:
logger.warning(
f"Error try to look for the db_stats files for server : {ex}"
)
@staticmethod
def get_all_servers_stats():
servers = HelperServers.get_all_defined_servers()
server_data = []
try:
for s in servers:
HelperServerStats.select_database(s.get("server_id"))
latest = (
ServerStats.select()
.where(ServerStats.server_id == s.get("server_id"))
.order_by(ServerStats.created.desc())
.limit(1)
)
server_data.append(
{
"server_data": s,
"stats": DatabaseShortcuts.return_rows(latest)[0],
"user_command_permission": True,
}
)
except IndexError as ex:
logger.error(
f"Stats collection failed with error: {ex}. Was a server just created?"
)
return server_data
@staticmethod
def insert_server_stats(server):
server_id = server.get("id", 0)
HelperServerStats.select_database(server_id)
if server_id == 0:
logger.warning("Stats saving failed with error: Server unknown (id = 0)")
return
ServerStats.insert(
{
ServerStats.server_id: server.get("id", 0),
ServerStats.started: server.get("started", ""),
ServerStats.running: server.get("running", False),
ServerStats.cpu: server.get("cpu", 0),
ServerStats.mem: server.get("mem", 0),
ServerStats.mem_percent: server.get("mem_percent", 0),
ServerStats.world_name: server.get("world_name", ""),
ServerStats.world_size: server.get("world_size", ""),
ServerStats.server_port: server.get("server_port", ""),
ServerStats.int_ping_results: server.get("int_ping_results", False),
ServerStats.online: server.get("online", False),
ServerStats.max: server.get("max", False),
ServerStats.players: server.get("players", False),
ServerStats.desc: server.get("desc", False),
ServerStats.version: server.get("version", False),
}
).execute()
@staticmethod
def remove_old_stats(server_id, last_week):
HelperServerStats.select_database(server_id)
ServerStats.delete().where(ServerStats.created < last_week).execute()
@staticmethod
def get_latest_server_stats(server_id):
HelperServerStats.select_database(server_id)
return (
ServerStats.select()
.where(ServerStats.server_id == server_id)
.order_by(ServerStats.created.desc())
.limit(1)
)
@staticmethod
def get_server_stats_by_id(server_id):
HelperServerStats.select_database(server_id)
stats = (
ServerStats.select()
.where(ServerStats.server_id == server_id)
.order_by(ServerStats.created.desc())
.limit(1)
)
return DatabaseShortcuts.return_rows(stats)[0]
@staticmethod
def server_id_exists(server_id):
HelperServerStats.select_database(server_id)
if not HelperServers.get_server_data_by_id(server_id):
return False
return True
@staticmethod
def sever_crashed(server_id):
HelperServerStats.select_database(server_id)
with database_stats_proxy.atomic():
ServerStats.update(crashed=True).where(
ServerStats.server_id == server_id
).execute()
@staticmethod
def set_download(server_id):
HelperServerStats.select_database(server_id)
with database_stats_proxy.atomic():
ServerStats.update(downloading=True).where(
ServerStats.server_id == server_id
).execute()
@staticmethod
def finish_download(server_id):
HelperServerStats.select_database(server_id)
with database_stats_proxy.atomic():
ServerStats.update(downloading=False).where(
ServerStats.server_id == server_id
).execute()
@staticmethod
def get_download_status(server_id):
HelperServerStats.select_database(server_id)
download_status = (
ServerStats.select().where(ServerStats.server_id == server_id).get()
)
return download_status.downloading
@staticmethod
def server_crash_reset(server_id):
if server_id is None:
return
HelperServerStats.select_database(server_id)
with database_stats_proxy.atomic():
ServerStats.update(crashed=False).where(
ServerStats.server_id == server_id
).execute()
@staticmethod
def is_crashed(server_id):
HelperServerStats.select_database(server_id)
svr = ServerStats.select().where(ServerStats.server_id == server_id).get()
# pylint: disable=singleton-comparison
if svr.crashed == True:
return True
else:
return False
@staticmethod
def set_update(server_id, value):
if server_id is None:
return
HelperServerStats.select_database(server_id)
try:
# Checks if server even exists
ServerStats.select().where(ServerStats.server_id == server_id)
except Exception as ex:
logger.error(f"Database entry not found! {ex}")
with database_stats_proxy.atomic():
ServerStats.update(updating=value).where(
ServerStats.server_id == server_id
).execute()
@staticmethod
def get_update_status(server_id):
HelperServerStats.select_database(server_id)
update_status = (
ServerStats.select().where(ServerStats.server_id == server_id).get()
)
return update_status.updating
@staticmethod
def set_first_run(server_id):
HelperServerStats.select_database(server_id)
# Sets first run to false
try:
# Checks if server even exists
ServerStats.select().where(ServerStats.server_id == server_id)
except Exception as ex:
logger.error(f"Database entry not found! {ex}")
return
with database_stats_proxy.atomic():
ServerStats.update(first_run=False).where(
ServerStats.server_id == server_id
).execute()
@staticmethod
def get_first_run(server_id):
HelperServerStats.select_database(server_id)
first_run = ServerStats.select().where(ServerStats.server_id == server_id).get()
return first_run.first_run
@staticmethod
def get_ttl_without_player(server_id):
HelperServerStats.select_database(server_id)
last_stat = (
ServerStats.select()
.where(ServerStats.server_id == server_id)
.order_by(ServerStats.created.desc())
.first()
)
last_stat_with_player = (
ServerStats.select()
.where(ServerStats.server_id == server_id)
.where(ServerStats.online > 0)
.order_by(ServerStats.created.desc())
.first()
)
return last_stat.created - last_stat_with_player.created
@staticmethod
def can_stop_no_players(server_id, time_limit):
HelperServerStats.select_database(server_id)
can = False
ttl_no_players = HelperServerStats.get_ttl_without_player(server_id)
if (time_limit == -1) or (ttl_no_players > time_limit):
can = True
return can
@staticmethod
def set_waiting_start(server_id, value):
HelperServerStats.select_database(server_id)
try:
# Checks if server even exists
ServerStats.select().where(ServerStats.server_id == server_id)
except Exception as ex:
logger.error(f"Database entry not found! {ex}")
with database_stats_proxy.atomic():
ServerStats.update(waiting_start=value).where(
ServerStats.server_id == server_id
).execute()
@staticmethod
def get_waiting_start(server_id):
HelperServerStats.select_database(server_id)
waiting_start = (
ServerStats.select().where(ServerStats.server_id == server_id).get()
)
return waiting_start.waiting_start

View File

@ -1,36 +1,22 @@
import logging
import datetime
from peewee import (
CharField,
AutoField,
DateTimeField,
BooleanField,
IntegerField,
)
from app.classes.shared.helpers import helper
from app.classes.shared.main_models import db_helper
try:
from peewee import (
SqliteDatabase,
Model,
ForeignKeyField,
CharField,
AutoField,
DateTimeField,
BooleanField,
IntegerField,
FloatField,
)
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
from app.classes.shared.main_models import DatabaseShortcuts
from app.classes.models.base_model import BaseModel
logger = logging.getLogger(__name__)
peewee_logger = logging.getLogger("peewee")
peewee_logger.setLevel(logging.INFO)
database = SqliteDatabase(
helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
)
# **********************************************************************************
# Servers Class
# **********************************************************************************
class Servers(Model):
class Servers(BaseModel):
server_id = AutoField()
created = DateTimeField(default=datetime.datetime.now)
server_uuid = CharField(default="", index=True)
@ -52,45 +38,14 @@ class Servers(Model):
class Meta:
table_name = "servers"
database = database
# **********************************************************************************
# Servers Stats Class
# **********************************************************************************
class Server_Stats(Model):
stats_id = AutoField()
created = DateTimeField(default=datetime.datetime.now)
server_id = ForeignKeyField(Servers, backref="server", index=True)
started = CharField(default="")
running = BooleanField(default=False)
cpu = FloatField(default=0)
mem = FloatField(default=0)
mem_percent = FloatField(default=0)
world_name = CharField(default="")
world_size = CharField(default="")
server_port = IntegerField(default=25565)
int_ping_results = CharField(default="")
online = IntegerField(default=0)
max = IntegerField(default=0)
players = CharField(default="")
desc = CharField(default="Unable to Connect")
version = CharField(default="")
updating = BooleanField(default=False)
waiting_start = BooleanField(default=False)
first_run = BooleanField(default=True)
crashed = BooleanField(default=False)
downloading = BooleanField(default=False)
class Meta:
table_name = "server_stats"
database = database
# **********************************************************************************
# Servers Class
# **********************************************************************************
class helper_servers:
class HelperServers:
def __init__(self, database):
self.database = database
# **********************************************************************************
# Generic Servers Methods
@ -139,16 +94,15 @@ class helper_servers:
def update_server(server_obj):
return server_obj.save()
@staticmethod
def remove_server(server_id):
with database.atomic():
def remove_server(self, server_id):
with self.database.atomic():
Servers.delete().where(Servers.server_id == server_id).execute()
@staticmethod
def get_server_data_by_id(server_id):
query = Servers.select().where(Servers.server_id == server_id).limit(1)
try:
return db_helper.return_rows(query)[0]
return DatabaseShortcuts.return_rows(query)[0]
except IndexError:
return {}
@ -158,197 +112,13 @@ class helper_servers:
@staticmethod
def get_all_defined_servers():
query = Servers.select()
return db_helper.return_rows(query)
@staticmethod
def get_all_servers_stats():
servers = servers_helper.get_all_defined_servers()
server_data = []
try:
for s in servers:
latest = (
Server_Stats.select()
.where(Server_Stats.server_id == s.get("server_id"))
.order_by(Server_Stats.created.desc())
.limit(1)
)
server_data.append(
{
"server_data": s,
"stats": db_helper.return_rows(latest)[0],
"user_command_permission": True,
}
)
except IndexError as ex:
logger.error(
f"Stats collection failed with error: {ex}. Was a server just created?"
)
return server_data
return DatabaseShortcuts.return_rows(query)
@staticmethod
def get_server_friendly_name(server_id):
server_data = servers_helper.get_server_data_by_id(server_id)
server_data = HelperServers.get_server_data_by_id(server_id)
friendly_name = (
f"{server_data.get('server_name', None)} "
f"with ID: {server_data.get('server_id', 0)}"
)
return friendly_name
# **********************************************************************************
# Servers_Stats Methods
# **********************************************************************************
@staticmethod
def get_latest_server_stats(server_id):
return (
Server_Stats.select()
.where(Server_Stats.server_id == server_id)
.order_by(Server_Stats.created.desc())
.limit(1)
)
@staticmethod
def get_server_stats_by_id(server_id):
stats = (
Server_Stats.select()
.where(Server_Stats.server_id == server_id)
.order_by(Server_Stats.created.desc())
.limit(1)
)
return db_helper.return_rows(stats)[0]
@staticmethod
def server_id_exists(server_id):
if not servers_helper.get_server_data_by_id(server_id):
return False
return True
@staticmethod
def sever_crashed(server_id):
with database.atomic():
Server_Stats.update(crashed=True).where(
Server_Stats.server_id == server_id
).execute()
@staticmethod
def set_download(server_id):
with database.atomic():
Server_Stats.update(downloading=True).where(
Server_Stats.server_id == server_id
).execute()
@staticmethod
def finish_download(server_id):
with database.atomic():
Server_Stats.update(downloading=False).where(
Server_Stats.server_id == server_id
).execute()
@staticmethod
def get_download_status(server_id):
download_status = (
Server_Stats.select().where(Server_Stats.server_id == server_id).get()
)
return download_status.downloading
@staticmethod
def server_crash_reset(server_id):
with database.atomic():
Server_Stats.update(crashed=False).where(
Server_Stats.server_id == server_id
).execute()
@staticmethod
def is_crashed(server_id):
svr = Server_Stats.select().where(Server_Stats.server_id == server_id).get()
# pylint: disable=singleton-comparison
if svr.crashed == True:
return True
else:
return False
@staticmethod
def set_update(server_id, value):
try:
# Checks if server even exists
Server_Stats.select().where(Server_Stats.server_id == server_id)
except Exception as ex:
logger.error(f"Database entry not found! {ex}")
with database.atomic():
Server_Stats.update(updating=value).where(
Server_Stats.server_id == server_id
).execute()
@staticmethod
def get_update_status(server_id):
update_status = (
Server_Stats.select().where(Server_Stats.server_id == server_id).get()
)
return update_status.updating
@staticmethod
def set_first_run(server_id):
# Sets first run to false
try:
# Checks if server even exists
Server_Stats.select().where(Server_Stats.server_id == server_id)
except Exception as ex:
logger.error(f"Database entry not found! {ex}")
return
with database.atomic():
Server_Stats.update(first_run=False).where(
Server_Stats.server_id == server_id
).execute()
@staticmethod
def get_first_run(server_id):
first_run = (
Server_Stats.select().where(Server_Stats.server_id == server_id).get()
)
return first_run.first_run
@staticmethod
def get_TTL_without_player(server_id):
last_stat = (
Server_Stats.select()
.where(Server_Stats.server_id == server_id)
.order_by(Server_Stats.created.desc())
.first()
)
last_stat_with_player = (
Server_Stats.select()
.where(Server_Stats.server_id == server_id)
.where(Server_Stats.online > 0)
.order_by(Server_Stats.created.desc())
.first()
)
return last_stat.created - last_stat_with_player.created
@staticmethod
def can_stop_no_players(server_id, time_limit):
can = False
ttl_no_players = servers_helper.get_TTL_without_player(server_id)
if (time_limit == -1) or (ttl_no_players > time_limit):
can = True
return can
@staticmethod
def set_waiting_start(server_id, value):
try:
# Checks if server even exists
Server_Stats.select().where(Server_Stats.server_id == server_id)
except Exception as ex:
logger.error(f"Database entry not found! {ex}")
with database.atomic():
Server_Stats.update(waiting_start=value).where(
Server_Stats.server_id == server_id
).execute()
@staticmethod
def get_waiting_start(server_id):
waiting_start = (
Server_Stats.select().where(Server_Stats.server_id == server_id).get()
)
return waiting_start.waiting_start
servers_helper = helper_servers()

View File

@ -2,38 +2,28 @@ import logging
import datetime
from typing import Optional, Union
from app.classes.models.roles import Roles, roles_helper
from app.classes.shared.helpers import helper
from peewee import (
ForeignKeyField,
CharField,
AutoField,
DateTimeField,
BooleanField,
CompositeKey,
DoesNotExist,
JOIN,
)
from playhouse.shortcuts import model_to_dict
try:
from peewee import (
SqliteDatabase,
Model,
ForeignKeyField,
CharField,
AutoField,
DateTimeField,
BooleanField,
CompositeKey,
DoesNotExist,
JOIN,
)
from playhouse.shortcuts import model_to_dict
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
from app.classes.shared.helpers import Helpers
from app.classes.models.base_model import BaseModel
from app.classes.models.roles import Roles, HelperRoles
logger = logging.getLogger(__name__)
peewee_logger = logging.getLogger("peewee")
peewee_logger.setLevel(logging.INFO)
database = SqliteDatabase(
helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
)
# **********************************************************************************
# Users Class
# **********************************************************************************
class Users(Model):
class Users(BaseModel):
user_id = AutoField()
created = DateTimeField(default=datetime.datetime.now)
last_login = DateTimeField(default=datetime.datetime.now)
@ -49,16 +39,16 @@ class Users(Model):
valid_tokens_from = DateTimeField(default=datetime.datetime.now)
server_order = CharField(default="")
preparing = BooleanField(default=False)
hints = BooleanField(default=True)
class Meta:
table_name = "users"
database = database
# **********************************************************************************
# API Keys Class
# **********************************************************************************
class ApiKeys(Model):
class ApiKeys(BaseModel):
token_id = AutoField()
name = CharField(default="", unique=True, index=True)
created = DateTimeField(default=datetime.datetime.now)
@ -69,26 +59,28 @@ class ApiKeys(Model):
class Meta:
table_name = "api_keys"
database = database
# **********************************************************************************
# User Roles Class
# **********************************************************************************
class User_Roles(Model):
class UserRoles(BaseModel):
user_id = ForeignKeyField(Users, backref="user_role")
role_id = ForeignKeyField(Roles, backref="user_role")
class Meta:
table_name = "user_roles"
primary_key = CompositeKey("user_id", "role_id")
database = database
# **********************************************************************************
# Users Helpers
# **********************************************************************************
class helper_users:
class HelperUsers:
def __init__(self, database, helper):
self.database = database
self.helper = helper
@staticmethod
def get_by_id(user_id):
return Users.get_by_id(user_id)
@ -136,7 +128,7 @@ class helper_users:
if user:
# I know it should apply it without setting it but I'm just making sure
user = users_helper.add_user_roles(user)
user = HelperUsers.add_user_roles(user)
return user
else:
# logger.debug("user: ({}) {}".format(user_id, {}))
@ -154,11 +146,11 @@ class helper_users:
@staticmethod
def get_user_model(user_id: str) -> Users:
user = Users.get(Users.user_id == user_id)
user = users_helper.add_user_roles(user)
user = HelperUsers.add_user_roles(user)
return user
@staticmethod
def add_user(
self,
username: str,
password: str = None,
email: Optional[str] = None,
@ -166,7 +158,7 @@ class helper_users:
superuser: bool = False,
) -> str:
if password is not None:
pw_enc = helper.encode_pass(password)
pw_enc = self.helper.encode_pass(password)
else:
pw_enc = None
user_id = Users.insert(
@ -176,7 +168,7 @@ class helper_users:
Users.email: email,
Users.enabled: enabled,
Users.superuser: superuser,
Users.created: helper.get_time_as_string(),
Users.created: Helpers.get_time_as_string(),
}
).execute()
return user_id
@ -196,7 +188,7 @@ class helper_users:
Users.email: email,
Users.enabled: enabled,
Users.superuser: superuser,
Users.created: helper.get_time_as_string(),
Users.created: Helpers.get_time_as_string(),
}
).execute()
return user_id
@ -221,17 +213,17 @@ class helper_users:
@staticmethod
def get_super_user_list():
final_users = []
# pylint: disable=singleton-comparison
super_users = Users.select().where(Users.superuser == True)
super_users = Users.select().where(
Users.superuser == True # pylint: disable=singleton-comparison
)
for suser in super_users:
if suser.user_id not in final_users:
final_users.append(suser.user_id)
return final_users
@staticmethod
def remove_user(user_id):
with database.atomic():
User_Roles.delete().where(User_Roles.user_id == user_id).execute()
def remove_user(self, user_id):
with self.database.atomic():
UserRoles.delete().where(UserRoles.user_id == user_id).execute()
user = Users.get(Users.user_id == user_id)
return user.delete_instance()
@ -251,12 +243,13 @@ class helper_users:
@staticmethod
def clear_support_status():
# pylint: disable=singleton-comparison
Users.update(preparing=False).where(Users.preparing == True).execute()
Users.update(preparing=False).where(
Users.preparing == True # pylint: disable=singleton-comparison
).execute()
@staticmethod
def user_id_exists(user_id):
if not users_helper.get_user(user_id):
if not HelperUsers.get_user(user_id):
return False
return True
@ -266,28 +259,28 @@ class helper_users:
@staticmethod
def get_or_create(user_id, role_id):
return User_Roles.get_or_create(user_id=user_id, role_id=role_id)
return UserRoles.get_or_create(user_id=user_id, role_id=role_id)
@staticmethod
def get_user_roles_id(user_id):
roles_list = []
roles = User_Roles.select().where(User_Roles.user_id == user_id)
roles = UserRoles.select().where(UserRoles.user_id == user_id)
for r in roles:
roles_list.append(roles_helper.get_role(r.role_id)["role_id"])
roles_list.append(HelperRoles.get_role(r.role_id)["role_id"])
return roles_list
@staticmethod
def get_user_roles_names(user_id):
roles_list = []
roles = User_Roles.select().where(User_Roles.user_id == user_id)
roles = UserRoles.select().where(UserRoles.user_id == user_id)
for r in roles:
roles_list.append(roles_helper.get_role(r.role_id)["role_name"])
roles_list.append(HelperRoles.get_role(r.role_id)["role_name"])
return roles_list
@staticmethod
def add_role_to_user(user_id, role_id):
User_Roles.insert(
{User_Roles.user_id: user_id, User_Roles.role_id: role_id}
UserRoles.insert(
{UserRoles.user_id: user_id, UserRoles.role_id: role_id}
).execute()
@staticmethod
@ -301,9 +294,9 @@ class helper_users:
# it had those TODOs & comments made by mac - Lukas
roles_query = (
User_Roles.select()
UserRoles.select()
.join(Roles, JOIN.INNER)
.where(User_Roles.user_id == user_id)
.where(UserRoles.user_id == user_id)
)
# TODO: this query needs to be narrower
roles = set()
@ -320,21 +313,21 @@ class helper_users:
@staticmethod
def user_role_query(user_id):
user_query = User_Roles.select().where(User_Roles.user_id == user_id)
user_query = UserRoles.select().where(UserRoles.user_id == user_id)
query = Roles.select().where(Roles.role_id == -1)
for u in user_query:
query = query + Roles.select().where(Roles.role_id == u.role_id)
for user in user_query:
query = query + Roles.select().where(Roles.role_id == user.role_id)
return query
@staticmethod
def delete_user_roles(user_id, removed_roles):
User_Roles.delete().where(User_Roles.user_id == user_id).where(
User_Roles.role_id.in_(removed_roles)
UserRoles.delete().where(UserRoles.user_id == user_id).where(
UserRoles.role_id.in_(removed_roles)
).execute()
@staticmethod
def remove_roles_from_role_id(role_id):
User_Roles.delete().where(User_Roles.role_id == role_id).execute()
UserRoles.delete().where(UserRoles.role_id == role_id).execute()
# **********************************************************************************
# ApiKeys Methods
@ -381,6 +374,3 @@ class helper_users:
@staticmethod
def delete_user_api_key(key_id: str):
ApiKeys.delete().where(ApiKeys.token_id == key_id).execute()
users_helper = helper_users()

View File

@ -1,56 +1,50 @@
import logging
import time
from typing import Optional, Dict, Any, Tuple
import jwt
from jwt import PyJWTError
from app.classes.models.users import users_helper, ApiKeys
from app.classes.shared.helpers import helper
try:
import jwt
from jwt import PyJWTError
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
from app.classes.models.users import HelperUsers, ApiKeys
logger = logging.getLogger(__name__)
class Authentication:
def __init__(self):
def __init__(self, helper):
self.helper = helper
self.secret = "my secret"
self.secret = helper.get_setting("apikey_secret", None)
self.secret = self.helper.get_setting("apikey_secret", None)
if self.secret is None or self.secret == "random":
self.secret = helper.random_string_generator(64)
self.secret = self.helper.random_string_generator(64)
self.helper.set_setting("apikey_secret", self.secret)
@staticmethod
def generate(user_id, extra=None):
def generate(self, user_id, extra=None):
if extra is None:
extra = {}
return jwt.encode(
jwt_encoded = jwt.encode(
{"user_id": user_id, "iat": int(time.time()), **extra},
authentication.secret,
self.secret,
algorithm="HS256",
)
return jwt_encoded
@staticmethod
def read(token):
return jwt.decode(token, authentication.secret, algorithms=["HS256"])
def read(self, token):
return jwt.decode(token, self.secret, algorithms=["HS256"])
@staticmethod
def check_no_iat(token) -> Optional[Dict[str, Any]]:
def check_no_iat(self, token) -> Optional[Dict[str, Any]]:
try:
return jwt.decode(token, authentication.secret, algorithms=["HS256"])
return jwt.decode(token, self.secret, algorithms=["HS256"])
except PyJWTError as error:
logger.debug("Error while checking JWT token: ", exc_info=error)
return None
@staticmethod
def check(
self,
token,
) -> Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]:
try:
data = jwt.decode(token, authentication.secret, algorithms=["HS256"])
data = jwt.decode(token, self.secret, algorithms=["HS256"])
except PyJWTError as error:
logger.debug("Error while checking JWT token: ", exc_info=error)
return None
@ -58,11 +52,11 @@ class Authentication:
key: Optional[ApiKeys] = None
if "token_id" in data:
key_id = data["token_id"]
key = users_helper.get_user_api_key(key_id)
key = HelperUsers.get_user_api_key(key_id)
if key is None:
return None
user_id: str = data["user_id"]
user = users_helper.get_user(user_id)
user = HelperUsers.get_user(user_id)
# TODO: Have a cache or something so we don't constantly
# have to query the database
if int(user.get("valid_tokens_from").timestamp()) < iat:
@ -71,9 +65,5 @@ class Authentication:
else:
return None
@staticmethod
def check_bool(token) -> bool:
return authentication.check(token) is not None
authentication = Authentication()
def check_bool(self, token) -> bool:
return self.check(token) is not None

View File

@ -3,30 +3,28 @@ import cmd
import time
import threading
import logging
from app.classes.shared.console import Console
from app.classes.shared.console import console
from app.classes.shared.helpers import helper
from app.classes.shared.import3 import import3
from app.classes.web.websocket_helper import websocket_helper
from app.classes.shared.import3 import Import3
logger = logging.getLogger(__name__)
class MainPrompt(cmd.Cmd):
def __init__(self, tasks_manager, migration_manager):
def __init__(self, helper, tasks_manager, migration_manager):
super().__init__()
self.helper = helper
self.tasks_manager = tasks_manager
self.migration_manager = migration_manager
# overrides the default Prompt
self.prompt = f"Crafty Controller v{self.helper.get_version_string()} > "
# overrides the default Prompt
prompt = f"Crafty Controller v{helper.get_version_string()} > "
# see MR !233 for pylint exemptino reason
@staticmethod
def emptyline():
def emptyline(): # pylint: disable=arguments-differ
pass
# pylint: disable=unused-argument
def do_exit(self, line):
def do_exit(self, _line):
self.tasks_manager._main_graceful_exit()
self.universal_exit()
@ -36,20 +34,20 @@ class MainPrompt(cmd.Cmd):
elif line == "down":
self.migration_manager.down()
elif line == "done":
console.info(self.migration_manager.done)
Console.info(self.migration_manager.done)
elif line == "todo":
console.info(self.migration_manager.todo)
Console.info(self.migration_manager.todo)
elif line == "diff":
console.info(self.migration_manager.diff)
Console.info(self.migration_manager.diff)
elif line == "info":
console.info(f"Done: {self.migration_manager.done}")
console.info(f"FS: {self.migration_manager.todo}")
console.info(f"Todo: {self.migration_manager.diff}")
Console.info(f"Done: {self.migration_manager.done}")
Console.info(f"FS: {self.migration_manager.todo}")
Console.info(f"Todo: {self.migration_manager.diff}")
elif line.startswith("add "):
migration_name = line[len("add ") :]
self.migration_manager.create(migration_name, False)
else:
console.info("Unknown migration command")
Console.info("Unknown migration command")
@staticmethod
def do_threads(_line):
@ -63,28 +61,25 @@ class MainPrompt(cmd.Cmd):
print(f"Name: {thread.name} Identifier: {thread.ident}")
def do_import3(self, _line):
import3.start_import()
Import3.start_import()
def universal_exit(self):
logger.info("Stopping all server daemons / threads")
console.info(
Console.info(
"Stopping all server daemons / threads - This may take a few seconds"
)
websocket_helper.disconnect_all()
console.info("Waiting for main thread to stop")
self.helper.websocket_helper.disconnect_all()
Console.info("Waiting for main thread to stop")
while True:
if self.tasks_manager.get_main_thread_run_status():
sys.exit(0)
time.sleep(1)
@staticmethod
def help_exit():
console.help("Stops the server if running, Exits the program")
def help_exit(self):
Console.help("Stops the server if running, Exits the program")
@staticmethod
def help_migrations():
console.help("Only for advanced users. Use with caution")
def help_migrations(self):
Console.help("Only for advanced users. Use with caution")
@staticmethod
def help_import3():
console.help("Import users and servers from Crafty 3")
def help_import3(self):
Console.help("Import users and servers from Crafty 3")

View File

@ -28,47 +28,56 @@ class Console:
else:
print(message)
def magenta(self, message):
self.do_print(message, "magenta")
@staticmethod
def magenta(message):
Console.do_print(message, "magenta")
def cyan(self, message):
self.do_print(message, "cyan")
@staticmethod
def cyan(message):
Console.do_print(message, "cyan")
def yellow(self, message):
self.do_print(message, "yellow")
@staticmethod
def yellow(message):
Console.do_print(message, "yellow")
def red(self, message):
self.do_print(message, "red")
@staticmethod
def red(message):
Console.do_print(message, "red")
def green(self, message):
self.do_print(message, "green")
@staticmethod
def green(message):
Console.do_print(message, "green")
def white(self, message):
self.do_print(message, "white")
@staticmethod
def white(message):
Console.do_print(message, "white")
def debug(self, message):
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
self.magenta(f"[+] Crafty: {dt} - DEBUG:\t{message}")
@staticmethod
def debug(message):
date_time = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
Console.magenta(f"[+] Crafty: {date_time} - DEBUG:\t{message}")
def info(self, message):
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
self.white(f"[+] Crafty: {dt} - INFO:\t{message}")
@staticmethod
def info(message):
date_time = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
Console.white(f"[+] Crafty: {date_time} - INFO:\t{message}")
def warning(self, message):
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
self.cyan(f"[+] Crafty: {dt} - WARNING:\t{message}")
@staticmethod
def warning(message):
date_time = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
Console.cyan(f"[+] Crafty: {date_time} - WARNING:\t{message}")
def error(self, message):
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
self.yellow(f"[+] Crafty: {dt} - ERROR:\t{message}")
@staticmethod
def error(message):
date_time = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
Console.yellow(f"[+] Crafty: {date_time} - ERROR:\t{message}")
def critical(self, message):
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
self.red(f"[+] Crafty: {dt} - CRITICAL:\t{message}")
@staticmethod
def critical(message):
date_time = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
Console.red(f"[+] Crafty: {date_time} - CRITICAL:\t{message}")
def help(self, message):
dt = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
self.green(f"[+] Crafty: {dt} - HELP:\t{message}")
console = Console()
@staticmethod
def help(message):
date_time = datetime.datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
Console.green(f"[+] Crafty: {date_time} - HELP:\t{message}")

View File

@ -10,12 +10,13 @@ logger = logging.getLogger(__name__)
class FileHelpers:
allowed_quotes = ['"', "'", "`"]
def del_dirs(self, path):
@staticmethod
def del_dirs(path):
path = pathlib.Path(path)
for sub in path.iterdir():
if sub.is_dir():
# Delete folder if it is a folder
self.del_dirs(sub)
FileHelpers.del_dirs(sub)
else:
# Delete file if it is a file:
sub.unlink()
@ -45,31 +46,33 @@ class FileHelpers:
def copy_file(src_path, dest_path):
shutil.copy(src_path, dest_path)
def move_dir(self, src_path, dest_path):
self.copy_dir(src_path, dest_path)
self.del_dirs(src_path)
@staticmethod
def move_dir(src_path, dest_path):
FileHelpers.copy_dir(src_path, dest_path)
FileHelpers.del_dirs(src_path)
def move_file(self, src_path, dest_path):
self.copy_file(src_path, dest_path)
self.del_file(src_path)
@staticmethod
def move_file(src_path, dest_path):
FileHelpers.copy_file(src_path, dest_path)
FileHelpers.del_file(src_path)
@staticmethod
def make_archive(path_to_destination, path_to_zip):
# create a ZipFile object
path_to_destination += ".zip"
with ZipFile(path_to_destination, "w") as z:
with ZipFile(path_to_destination, "w") as zip_file:
for root, _dirs, files in os.walk(path_to_zip, topdown=True):
ziproot = path_to_zip
for file in files:
try:
logger.info(f"backing up: {os.path.join(root, file)}")
if os.name == "nt":
z.write(
zip_file.write(
os.path.join(root, file),
os.path.join(root.replace(ziproot, ""), file),
)
else:
z.write(
zip_file.write(
os.path.join(root, file),
os.path.join(root.replace(ziproot, "/"), file),
)
@ -86,19 +89,19 @@ class FileHelpers:
def make_compressed_archive(path_to_destination, path_to_zip):
# create a ZipFile object
path_to_destination += ".zip"
with ZipFile(path_to_destination, "w", ZIP_DEFLATED) as z:
with ZipFile(path_to_destination, "w", ZIP_DEFLATED) as zip_file:
for root, _dirs, files in os.walk(path_to_zip, topdown=True):
ziproot = path_to_zip
for file in files:
try:
logger.info(f"backing up: {os.path.join(root, file)}")
if os.name == "nt":
z.write(
zip_file.write(
os.path.join(root, file),
os.path.join(root.replace(ziproot, ""), file),
)
else:
z.write(
zip_file.write(
os.path.join(root, file),
os.path.join(root.replace(ziproot, "/"), file),
)
@ -110,6 +113,3 @@ class FileHelpers:
)
return True
file_helper = FileHelpers()

View File

@ -19,10 +19,11 @@ from socket import gethostname
from contextlib import suppress
import psutil
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.file_helpers import file_helper
from app.classes.web.websocket_helper import websocket_helper
from app.classes.shared.file_helpers import FileHelpers
from app.classes.shared.translation import Translation
from app.classes.web.websocket_helper import WebSocketHelper
logger = logging.getLogger(__name__)
@ -61,17 +62,22 @@ class Helpers:
self.passhasher = PasswordHasher()
self.exiting = False
self.websocket_helper = WebSocketHelper(self)
self.translation = Translation(self)
@staticmethod
def auto_installer_fix(ex):
logger.critical(f"Import Error: Unable to load {ex.name} module", exc_info=True)
print(f"Import Error: Unable to load {ex.name} module")
installer.do_install()
def float_to_string(self, gbs: int):
@staticmethod
def float_to_string(gbs: int):
s = str(float(gbs) * 1000).rstrip("0").rstrip(".")
return s
def check_file_perms(self, path):
@staticmethod
def check_file_perms(path):
try:
open(path, "r", encoding="utf-8").close()
logger.info(f"{path} is readable")
@ -79,8 +85,9 @@ class Helpers:
except PermissionError:
return False
def is_file_older_than_x_days(self, file, days=1):
if self.check_file_exists(file):
@staticmethod
def is_file_older_than_x_days(file, days=1):
if Helpers.check_file_exists(file):
file_time = os.path.getmtime(file)
# Check against 24 hours
if (time.time() - file_time) / 3600 > 24 * days:
@ -139,47 +146,51 @@ class Helpers:
def cmdparse(cmd_in):
# Parse a string into arguments
cmd_out = [] # "argv" output array
ci = -1 # command index - pointer to the argument we're building in cmd_out
np = True # whether we're creating a new argument/parameter
cmd_index = (
-1
) # command index - pointer to the argument we're building in cmd_out
new_param = True # whether we're creating a new argument/parameter
esc = False # whether an escape character was encountered
stch = None # if we're dealing with a quote, save the quote type here.
quote_char = None # if we're dealing with a quote, save the quote type here.
# Nested quotes to be dealt with by the command
for c in cmd_in: # for character in string
if np: # if set, begin a new argument and increment the command index.
for char in cmd_in: # for character in string
if (
new_param
): # if set, begin a new argument and increment the command index.
# Continue the loop.
if c == " ":
if char == " ":
continue
else:
ci += 1
cmd_index += 1
cmd_out.append("")
np = False
new_param = False
if esc: # if we encountered an escape character on the last loop,
# append this char regardless of what it is
if c not in Helpers.allowed_quotes:
cmd_out[ci] += "\\"
cmd_out[ci] += c
if char not in Helpers.allowed_quotes:
cmd_out[cmd_index] += "\\"
cmd_out[cmd_index] += char
esc = False
else:
if c == "\\": # if the current character is an escape character,
if char == "\\": # if the current character is an escape character,
# set the esc flag and continue to next loop
esc = True
elif (
c == " " and stch is None
char == " " and quote_char is None
): # if we encounter a space and are not dealing with a quote,
# set the new argument flag and continue to next loop
np = True
new_param = True
elif (
c == stch
char == quote_char
): # if we encounter the character that matches our start quote,
# end the quote and continue to next loop
stch = None
elif stch is None and (
c in Helpers.allowed_quotes
quote_char = None
elif quote_char is None and (
char in Helpers.allowed_quotes
): # if we're not in the middle of a quote and we get a quotable
# character, start a quote and proceed to the next loop
stch = c
quote_char = char
else: # else, just store the character in the current arg
cmd_out[ci] += c
cmd_out[cmd_index] += char
return cmd_out
def get_setting(self, key, default_return=False):
@ -193,30 +204,56 @@ class Helpers:
else:
logger.error(f"Config File Error: setting {key} does not exist")
console.error(f"Config File Error: setting {key} does not exist")
Console.error(f"Config File Error: setting {key} does not exist")
return default_return
except Exception as e:
logger.critical(
f"Config File Error: Unable to read {self.settings_file} due to {e}"
)
console.critical(
Console.critical(
f"Config File Error: Unable to read {self.settings_file} due to {e}"
)
return default_return
def get_local_ip(self):
def set_setting(self, key, new_value, default_return=False):
try:
with open(self.settings_file, "r", encoding="utf-8") as f:
data = json.load(f)
if key in data.keys():
data[key] = new_value
else:
logger.error(f"Config File Error: setting {key} does not exist")
Console.error(f"Config File Error: setting {key} does not exist")
return default_return
with open(self.settings_file, "w", encoding="utf-8") as f:
json.dump(data, f, indent=1)
except Exception as e:
logger.critical(
f"Config File Error: Unable to read {self.settings_file} due to {e}"
)
Console.critical(
f"Config File Error: Unable to read {self.settings_file} due to {e}"
)
@staticmethod
def get_local_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(("10.255.255.255", 1))
IP = s.getsockname()[0]
ip = s.getsockname()[0]
except Exception:
IP = "127.0.0.1"
ip = "127.0.0.1"
finally:
s.close()
return IP
return ip
def get_version(self):
version_data = {}
@ -227,29 +264,28 @@ class Helpers:
version_data = json.load(f)
except Exception as e:
console.critical(f"Unable to get version data! \n{e}")
Console.critical(f"Unable to get version data! \n{e}")
return version_data
@staticmethod
def get_announcements():
r = requests.get("https://craftycontrol.com/notify.json", timeout=2)
response = requests.get("https://craftycontrol.com/notify.json", timeout=2)
data = (
'[{"id":"1","date":"Unknown",'
'"title":"Error getting Announcements",'
'"desc":"Error getting Announcements","link":""}]'
)
if r.status_code in [200, 201]:
if response.status_code in [200, 201]:
try:
data = json.loads(r.content)
data = json.loads(response.content)
except Exception as e:
logger.error(f"Failed to load json content with error: {e}")
return data
def get_version_string(self):
version_data = self.get_version()
major = version_data.get("major", "?")
minor = version_data.get("minor", "?")
@ -306,19 +342,21 @@ class Helpers:
return line
def validate_traversal(self, base_path, filename):
@staticmethod
def validate_traversal(base_path, filename):
logger.debug(f'Validating traversal ("{base_path}", "{filename}")')
base = pathlib.Path(base_path).resolve()
file = pathlib.Path(filename)
fileabs = base.joinpath(file).resolve()
cp = pathlib.Path(os.path.commonpath([base, fileabs]))
if base == cp:
common_path = pathlib.Path(os.path.commonpath([base, fileabs]))
if base == common_path:
return fileabs
else:
raise ValueError("Path traversal detected")
def tail_file(self, file_name, number_lines=20):
if not self.check_file_exists(file_name):
@staticmethod
def tail_file(file_name, number_lines=20):
if not Helpers.check_file_exists(file_name):
logger.warning(f"Unable to find file to tail: {file_name}")
return [f"Unable to find file to tail: {file_name}"]
@ -367,8 +405,9 @@ class Helpers:
logger.critical(f"Unable to write to {path} - Error: {e}")
return False
def checkRoot(self):
if self.is_os_windows():
@staticmethod
def check_root():
if Helpers.is_os_windows():
if ctypes.windll.shell32.IsUserAnAdmin() == 1:
return True
else:
@ -379,7 +418,8 @@ class Helpers:
else:
return False
def unzipFile(self, zip_path):
@staticmethod
def unzip_file(zip_path):
new_dir_list = zip_path.split("/")
new_dir = ""
for i in range(len(new_dir_list) - 1):
@ -388,28 +428,39 @@ class Helpers:
else:
new_dir += "/" + new_dir_list[i]
if helper.check_file_perms(zip_path) and os.path.isfile(zip_path):
helper.ensure_dir_exists(new_dir)
tempDir = tempfile.mkdtemp()
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(tempDir)
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 = tempDir
full_root_path = temp_dir
for item in os.listdir(full_root_path):
try:
file_helper.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}")
print(item)
if os.path.isdir(os.path.join(full_root_path, item)):
print("dir")
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:
print(ex)
else:
@ -422,7 +473,7 @@ class Helpers:
logger.info("Checking app directory writable")
writeable = self.check_writeable(self.root_dir)
writeable = Helpers.check_writeable(self.root_dir)
# if not writeable, let's bomb out
if not writeable:
@ -434,13 +485,13 @@ class Helpers:
with suppress(FileExistsError):
os.makedirs(os.path.join(self.root_dir, "logs"))
except Exception as e:
console.error(f"Failed to make logs directory with error: {e} ")
Console.error(f"Failed to make logs directory with error: {e} ")
# ensure the log file is there
try:
open(log_file, "a", encoding="utf-8").close()
except Exception as e:
console.critical(f"Unable to open log file! {e}")
Console.critical(f"Unable to open log file! {e}")
sys.exit(1)
# del any old session.lock file as this is a new session
@ -461,16 +512,19 @@ class Helpers:
source_size = 0
files_count = 0
for path, _dirs, files in os.walk(source_path):
for f in files:
fp = os.path.join(path, f)
source_size += os.stat(fp).st_size
for file in files:
full_path = os.path.join(path, file)
source_size += os.stat(full_path).st_size
files_count += 1
dest_size = os.path.getsize(str(dest_path))
percent = round((dest_size / source_size) * 100, 1)
try:
dest_size = os.path.getsize(str(dest_path))
percent = round((dest_size / source_size) * 100, 1)
except:
percent = 0
if percent >= 0:
results = {"percent": percent, "total_files": files_count}
else:
results = {"percent": 0, "total_files": 0}
results = {"percent": 0, "total_files": files_count}
return results
@staticmethod
@ -540,7 +594,7 @@ class Helpers:
pid = data.get("pid")
started = data.get("started")
if psutil.pid_exists(pid):
console.critical(
Console.critical(
f"Another Crafty Controller agent seems to be running..."
f"\npid: {pid} \nstarted on: {started}"
)
@ -555,7 +609,7 @@ class Helpers:
except Exception as e:
logger.error(f"Failed to locate existing session.lock with error: {e} ")
console.error(
Console.error(
f"Failed to locate existing session.lock with error: {e} "
)
@ -570,11 +624,12 @@ class Helpers:
# because this is a recursive function, we will return bytes,
# and set human readable later
def get_dir_size(self, path: str):
@staticmethod
def get_dir_size(path: str):
total = 0
for entry in os.scandir(path):
if entry.is_dir(follow_symlinks=False):
total += self.get_dir_size(entry.path)
total += Helpers.get_dir_size(entry.path)
else:
total += entry.stat(follow_symlinks=False).st_size
return total
@ -588,11 +643,15 @@ class Helpers:
)
]
def get_human_readable_files_sizes(self, paths: list):
@staticmethod
def get_human_readable_files_sizes(paths: list):
sizes = []
for p in paths:
sizes.append(
{"path": p, "size": self.human_readable_file_size(os.stat(p).st_size)}
{
"path": p,
"size": Helpers.human_readable_file_size(os.stat(p).st_size),
}
)
return sizes
@ -608,10 +667,12 @@ class Helpers:
b64_bytes = base64.decodebytes(s_bytes)
return b64_bytes.decode("utf-8")
def create_uuid(self):
@staticmethod
def create_uuid():
return str(uuid.uuid4())
def ensure_dir_exists(self, path):
@staticmethod
def ensure_dir_exists(path):
"""
ensures a directory exists
@ -637,7 +698,7 @@ class Helpers:
cert_dir = os.path.join(self.config_dir, "web", "certs")
# create a directory if needed
self.ensure_dir_exists(cert_dir)
Helpers.ensure_dir_exists(cert_dir)
cert_file = os.path.join(cert_dir, "commander.cert.pem")
key_file = os.path.join(cert_dir, "commander.key.pem")
@ -646,16 +707,16 @@ class Helpers:
logger.info(f"SSL Key File is set to: {key_file}")
# don't create new files if we already have them.
if self.check_file_exists(cert_file) and self.check_file_exists(key_file):
if Helpers.check_file_exists(cert_file) and Helpers.check_file_exists(key_file):
logger.info("Cert and Key files already exists, not creating them.")
return True
console.info("Generating a self signed SSL")
Console.info("Generating a self signed SSL")
logger.info("Generating a self signed SSL")
# create a key pair
logger.info("Generating a key pair. This might take a moment.")
console.info("Generating a key pair. This might take a moment.")
Console.info("Generating a key pair. This might take a moment.")
k = crypto.PKey()
k.generate_key(crypto.TYPE_RSA, 4096)
@ -676,11 +737,13 @@ class Helpers:
"DNS:127.0.0.1",
]
).encode()
subjectAltNames_Ext = crypto.X509Extension(b"subjectAltName", False, alt_names)
basicConstraints_Ext = crypto.X509Extension(
subject_alt_names_ext = crypto.X509Extension(
b"subjectAltName", False, alt_names
)
basic_constraints_ext = crypto.X509Extension(
b"basicConstraints", True, b"CA:false"
)
cert.add_extensions([subjectAltNames_Ext, basicConstraints_Ext])
cert.add_extensions([subject_alt_names_ext, basic_constraints_ext])
cert.set_serial_number(random.randint(1, 255))
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(365 * 24 * 60 * 60)
@ -731,11 +794,11 @@ class Helpers:
default_file = os.path.join(self.root_dir, "default.json")
data = {}
if self.check_file_exists(default_file):
if Helpers.check_file_exists(default_file):
with open(default_file, "r", encoding="utf-8") as f:
data = json.load(f)
del_json = helper.get_setting("delete_default_json")
del_json = self.get_setting("delete_default_json")
if del_json:
os.remove(default_file)
@ -763,8 +826,8 @@ class Helpers:
output += f"""<li class="tree-item" data-path="{dpath}">
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
<i class="far fa-folder"></i>
<i class="far fa-folder-open"></i>
<i style="color: #8862e0;" class="far fa-folder"></i>
<i style="color: #8862e0;" class="far fa-folder-open"></i>
{filename}
</span>
</div><li>
@ -772,7 +835,7 @@ class Helpers:
else:
if filename != "crafty_managed.txt":
output += f"""<li
class="tree-nested d-block tree-ctx-item tree-file tree-item"
class="d-block tree-ctx-item tree-file tree-item"
data-path="{dpath}"
data-name="{filename}"
onclick="clickOnFile(event)"><span style="margin-right: 6px;">
@ -801,15 +864,15 @@ class Helpers:
output += f"""<li class="tree-item" data-path="{dpath}">
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
<i class="far fa-folder"></i>
<i class="far fa-folder-open"></i>
<i style="color: #8862e0;" class="far fa-folder"></i>
<i style="color: #8862e0;" class="far fa-folder-open"></i>
{filename}
</span>
</div><li>"""
else:
if filename != "crafty_managed.txt":
output += f"""<li
class="tree-nested d-block tree-ctx-item tree-file tree-item"
class="d-block tree-ctx-item tree-file tree-item"
data-path="{dpath}"
data-name="{filename}"
onclick="clickOnFile(event)"><span style="margin-right: 6px;">
@ -831,8 +894,8 @@ class Helpers:
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
<input type="radio" name="root_path" value="{dpath}">
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
<i class="far fa-folder"></i>
<i class="far fa-folder-open"></i>
<i style="color: #8862e0;" class="far fa-folder"></i>
<i style="color: #8862e0;" class="far fa-folder-open"></i>
{filename}
</span>
</input></div><li>
@ -853,39 +916,39 @@ class Helpers:
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
<input type="radio" name="root_path" value="{dpath}">
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
<i class="far fa-folder"></i>
<i class="far fa-folder-open"></i>
<i style="color: #8862e0;" class="far fa-folder"></i>
<i style="color: #8862e0;" class="far fa-folder-open"></i>
{filename}
</span>
</input></div><li>"""
return output
@staticmethod
def unzipServer(zip_path, user_id):
if helper.check_file_perms(zip_path):
tempDir = tempfile.mkdtemp()
def unzip_server(self, zip_path, user_id):
if Helpers.check_file_perms(zip_path):
temp_dir = tempfile.mkdtemp()
with zipfile.ZipFile(zip_path, "r") as zip_ref:
# extracts archive to temp directory
zip_ref.extractall(tempDir)
zip_ref.extractall(temp_dir)
if user_id:
websocket_helper.broadcast_user(
user_id, "send_temp_path", {"path": tempDir}
self.websocket_helper.broadcast_user(
user_id, "send_temp_path", {"path": temp_dir}
)
@staticmethod
def backup_select(path, user_id):
def backup_select(self, path, user_id):
if user_id:
websocket_helper.broadcast_user(user_id, "send_temp_path", {"path": path})
self.websocket_helper.broadcast_user(
user_id, "send_temp_path", {"path": path}
)
@staticmethod
def unzip_backup_archive(backup_path, zip_name):
zip_path = os.path.join(backup_path, zip_name)
if helper.check_file_perms(zip_path):
tempDir = tempfile.mkdtemp()
if Helpers.check_file_perms(zip_path):
temp_dir = tempfile.mkdtemp()
with zipfile.ZipFile(zip_path, "r") as zip_ref:
# extracts archive to temp directory
zip_ref.extractall(tempDir)
return tempDir
zip_ref.extractall(temp_dir)
return temp_dir
else:
return False
@ -905,14 +968,10 @@ class Helpers:
[parent_path, child_path]
)
@staticmethod
def in_path_old(x, y):
return os.path.abspath(y).__contains__(os.path.abspath(x))
@staticmethod
def copy_files(source, dest):
if os.path.isfile(source):
file_helper.copy_file(source, dest)
FileHelpers.copy_file(source, dest)
logger.info("Copying jar %s to %s", source, dest)
else:
logger.info("Source jar does not exist.")
@ -920,16 +979,16 @@ class Helpers:
@staticmethod
def download_file(executable_url, jar_path):
try:
r = requests.get(executable_url, timeout=5)
response = requests.get(executable_url, timeout=5)
except Exception as ex:
logger.error("Could not download executable: %s", ex)
return False
if r.status_code != 200:
if response.status_code != 200:
logger.error("Unable to download file from %s", executable_url)
return False
try:
open(jar_path, "wb").write(r.content)
open(jar_path, "wb").write(response.content)
except Exception as e:
logger.error("Unable to finish executable download. Error: %s", e)
return False
@ -942,13 +1001,10 @@ class Helpers:
return text
@staticmethod
def getLangPage(text):
def get_lang_page(text):
lang = text.split("_")[0]
region = text.split("_")[1]
if region == "EN":
return "en"
else:
return lang + "-" + region
helper = Helpers()

View File

@ -2,16 +2,16 @@ import json
import os
import logging
from app.classes.controllers.users_controller import users_helper
from app.classes.shared.main_controller import Controller
from app.classes.shared.console import console
from app.classes.controllers.users_controller import HelperUsers
from app.classes.shared.console import Console
logger = logging.getLogger(__name__)
class import3:
def __init__(self):
self.controller = Controller()
class Import3:
def __init__(self, helper, controller):
self.helper = helper
self.controller = controller
def start_import(self):
folder = os.path.normpath(
@ -21,11 +21,11 @@ class import3:
)
)
if not os.path.exists(folder):
console.info(
Console.info(
"Crafty cannot find the path you entered. "
"Does Crafty's user have permission to access it?"
)
console.info("Please run the import3 command again and enter a valid path.")
Console.info("Please run the import3 command again and enter a valid path.")
else:
with open(os.path.join(folder, "users.json"), encoding="utf-8") as f:
user_json = json.loads(f.read())
@ -34,16 +34,15 @@ class import3:
self.import_users(user_json)
self.import_servers(servers_json, self.controller)
@staticmethod
def import_users(json_data):
def import_users(self, json_data):
# If there is only one user to import json needs to call the data differently
if isinstance(json_data, list):
for user in json_data:
users_helper.add_rawpass_user(user["username"], user["password"])
console.info(f"Imported user {user['username']} from Crafty 3")
HelperUsers.add_rawpass_user(user["username"], user["password"])
Console.info(f"Imported user {user['username']} from Crafty 3")
logger.info(f"Imported user {user['username']} from Crafty 3")
else:
console.info(
Console.info(
"There is only one user detected. "
"Cannot create duplicate Admin account."
)
@ -52,8 +51,7 @@ class import3:
"Cannot create duplicate Admin account."
)
@staticmethod
def import_servers(json_data, controller):
def import_servers(self, json_data, controller):
# If there is only one server to import json needs to call the data differently
if isinstance(json_data, list):
for server in json_data:
@ -65,7 +63,7 @@ class import3:
max_mem=(int(server["memory_max"]) / 1000),
port=server["server_port"],
)
console.info(
Console.info(
f"Imported server {server['server_name']}[{server['id']}] "
f"from Crafty 3 to new server id {new_server_id}"
)
@ -82,7 +80,7 @@ class import3:
max_mem=(int(json_data["memory_max"]) / 1000),
port=json_data["server_port"],
)
console.info(
Console.info(
f"Imported server {json_data['server_name']}[{json_data['id']}] "
f"from Crafty 3 to new server id {new_server_id}"
)
@ -90,6 +88,3 @@ class import3:
f"Imported server {json_data['server_name']}[{json_data['id']}] "
f"from Crafty 3 to new server id {new_server_id}"
)
import3 = import3()

View File

@ -2,7 +2,7 @@ import sys
import subprocess
class install:
class Install:
@staticmethod
def is_venv():
return hasattr(sys, "real_prefix") or (
@ -24,4 +24,4 @@ class install:
sys.exit(0)
installer = install()
installer = Install()

View File

@ -6,49 +6,54 @@ import time
import logging
import tempfile
from typing import Union
from peewee import DoesNotExist
from app.classes.controllers.crafty_perms_controller import Crafty_Perms_Controller
from app.classes.controllers.management_controller import Management_Controller
from app.classes.controllers.users_controller import Users_Controller
from app.classes.controllers.roles_controller import Roles_Controller
from app.classes.controllers.server_perms_controller import Server_Perms_Controller
from app.classes.controllers.servers_controller import Servers_Controller
from app.classes.models.server_permissions import Enum_Permissions_Server
from app.classes.models.users import helper_users
from app.classes.models.management import helpers_management
from app.classes.models.servers import servers_helper
from app.classes.shared.console import console
from app.classes.shared.helpers import helper
# TZLocal is set as a hidden import on win pipeline
from tzlocal import get_localzone
from apscheduler.schedulers.background import BackgroundScheduler
from app.classes.controllers.crafty_perms_controller import CraftyPermsController
from app.classes.controllers.management_controller import ManagementController
from app.classes.controllers.users_controller import UsersController
from app.classes.controllers.roles_controller import RolesController
from app.classes.controllers.server_perms_controller import ServerPermsController
from app.classes.controllers.servers_controller import ServersController
from app.classes.models.server_permissions import EnumPermissionsServer
from app.classes.models.users import HelperUsers
from app.classes.models.roles import HelperRoles
from app.classes.models.management import HelpersManagement
from app.classes.models.servers import HelperServers
from app.classes.shared.authentication import Authentication
from app.classes.shared.console import Console
from app.classes.shared.helpers import Helpers
from app.classes.shared.server import Server
from app.classes.shared.file_helpers import file_helper
from app.classes.shared.file_helpers import FileHelpers
from app.classes.minecraft.server_props import ServerProps
from app.classes.minecraft.serverjars import server_jar_obj
from app.classes.minecraft.serverjars import ServerJars
from app.classes.minecraft.stats import Stats
from app.classes.web.websocket_helper import websocket_helper
try:
from peewee import DoesNotExist
# TZLocal is set as a hidden import on win pipeline
from tzlocal import get_localzone
from apscheduler.schedulers.background import BackgroundScheduler
except ModuleNotFoundError as err:
helper.auto_installer_fix(err)
logger = logging.getLogger(__name__)
class Controller:
def __init__(self):
def __init__(self, database, helper):
self.helper = helper
self.server_jars = ServerJars(helper)
self.users_helper = HelperUsers(database, self.helper)
self.roles_helper = HelperRoles(database)
self.servers_helper = HelperServers(database)
self.management_helper = HelpersManagement(database, self.helper)
self.authentication = Authentication(self.helper)
self.servers_list = []
self.stats = Stats(self)
self.crafty_perms = Crafty_Perms_Controller()
self.management = Management_Controller()
self.roles = Roles_Controller()
self.server_perms = Server_Perms_Controller()
self.servers = Servers_Controller()
self.users = Users_Controller()
self.stats = Stats(self.helper, self)
self.crafty_perms = CraftyPermsController()
self.management = ManagementController(self.management_helper)
self.roles = RolesController(self.users_helper, self.roles_helper)
self.server_perms = ServerPermsController()
self.servers = ServersController(self.servers_helper)
self.users = UsersController(
self.helper, self.users_helper, self.authentication
)
tz = get_localzone()
self.support_scheduler = BackgroundScheduler(timezone=str(tz))
self.support_scheduler.start()
@ -57,8 +62,8 @@ class Controller:
logger.info(f"Checking to see if we already registered {server_id_to_check}")
for s in self.servers_list:
known_server = s.get("server_id")
for server in self.servers_list:
known_server = server.get("server_id")
if known_server is None:
return False
@ -75,63 +80,65 @@ class Controller:
servers = self.servers.get_all_defined_servers()
for s in servers:
server_id = s.get("server_id")
for server in servers:
server_id = server.get("server_id")
# if we have already initialized this server, let's skip it.
if self.check_server_loaded(server_id):
continue
# if this server path no longer exists - let's warn and bomb out
if not helper.check_path_exists(
helper.get_os_understandable_path(s["path"])
if not Helpers.check_path_exists(
Helpers.get_os_understandable_path(server["path"])
):
logger.warning(
f"Unable to find server {s['server_name']} at path {s['path']}. "
f"Unable to find server "
f"{server['server_name']} at path {server['path']}. "
f"Skipping this server"
)
console.warning(
f"Unable to find server {s['server_name']} at path {s['path']}. "
Console.warning(
f"Unable to find server "
f"{server['server_name']} at path {server['path']}. "
f"Skipping this server"
)
continue
settings_file = os.path.join(
helper.get_os_understandable_path(s["path"]), "server.properties"
Helpers.get_os_understandable_path(server["path"]), "server.properties"
)
# if the properties file isn't there, let's warn
if not helper.check_file_exists(settings_file):
if not Helpers.check_file_exists(settings_file):
logger.error(f"Unable to find {settings_file}. Skipping this server.")
console.error(f"Unable to find {settings_file}. Skipping this server.")
Console.error(f"Unable to find {settings_file}. Skipping this server.")
continue
settings = ServerProps(settings_file)
temp_server_dict = {
"server_id": s.get("server_id"),
"server_data_obj": s,
"server_obj": Server(self.stats),
"server_id": server.get("server_id"),
"server_data_obj": server,
"server_obj": Server(self.helper, self.management_helper, self.stats),
"server_settings": settings.props,
}
# setup the server, do the auto start and all that jazz
temp_server_dict["server_obj"].do_server_setup(s)
temp_server_dict["server_obj"].do_server_setup(server)
# add this temp object to the list of init servers
self.servers_list.append(temp_server_dict)
if s["auto_start"]:
self.servers.set_waiting_start(s["server_id"], True)
if server["auto_start"]:
self.servers.set_waiting_start(server["server_id"], True)
self.refresh_server_settings(s["server_id"])
self.refresh_server_settings(server["server_id"])
console.info(
f"Loaded Server: ID {s['server_id']}"
+ f" | Name: {s['server_name']}"
+ f" | Autostart: {s['auto_start']}"
+ f" | Delay: {s['auto_start_delay']} "
Console.info(
f"Loaded Server: ID {server['server_id']}"
f" | Name: {server['server_name']}"
f" | Autostart: {server['auto_start']}"
f" | Delay: {server['auto_start_delay']}"
)
def refresh_server_settings(self, server_id: int):
@ -140,7 +147,7 @@ class Controller:
@staticmethod
def check_system_user():
if helper_users.get_user_id_by_name("system") is not None:
if HelperUsers.get_user_id_by_name("system") is not None:
return True
else:
return False
@ -154,14 +161,14 @@ class Controller:
self.users.set_prepare(exec_user["user_id"])
# pausing so on screen notifications can run for user
time.sleep(7)
websocket_helper.broadcast_user(
self.helper.websocket_helper.broadcast_user(
exec_user["user_id"], "notification", "Preparing your support logs"
)
tempDir = tempfile.mkdtemp()
tempZipStorage = tempfile.mkdtemp()
full_temp = os.path.join(tempDir, "support_logs")
temp_dir = tempfile.mkdtemp()
temp_zip_storage = tempfile.mkdtemp()
full_temp = os.path.join(temp_dir, "support_logs")
os.mkdir(full_temp)
tempZipStorage = os.path.join(tempZipStorage, "support_logs")
temp_zip_storage = os.path.join(temp_zip_storage, "support_logs")
crafty_path = os.path.join(full_temp, "crafty")
os.mkdir(crafty_path)
server_path = os.path.join(full_temp, "server")
@ -175,7 +182,7 @@ class Controller:
auth_servers = []
for server in user_servers:
if (
Enum_Permissions_Server.Logs
EnumPermissionsServer.LOGS
in self.server_perms.get_user_id_permissions_list(
exec_user["user_id"], server["server_id"]
)
@ -195,50 +202,51 @@ class Controller:
final_path += "_" + server["server_uuid"]
os.mkdir(final_path)
try:
file_helper.copy_file(server["log_path"], final_path)
FileHelpers.copy_file(server["log_path"], final_path)
except Exception as e:
logger.warning(f"Failed to copy file with error: {e}")
# Copy crafty logs to archive dir
full_log_name = os.path.join(crafty_path, "logs")
file_helper.copy_dir(os.path.join(self.project_root, "logs"), full_log_name)
FileHelpers.copy_dir(os.path.join(self.project_root, "logs"), full_log_name)
self.support_scheduler.add_job(
self.log_status,
"interval",
seconds=1,
id="logs_" + str(exec_user["user_id"]),
args=[full_temp, tempZipStorage + ".zip", exec_user],
args=[full_temp, temp_zip_storage + ".zip", exec_user],
)
file_helper.make_archive(tempZipStorage, tempDir)
FileHelpers.make_archive(temp_zip_storage, temp_dir)
if len(websocket_helper.clients) > 0:
websocket_helper.broadcast_user(
if len(self.helper.websocket_helper.clients) > 0:
self.helper.websocket_helper.broadcast_user(
exec_user["user_id"],
"support_status_update",
helper.calc_percent(full_temp, tempZipStorage + ".zip"),
Helpers.calc_percent(full_temp, temp_zip_storage + ".zip"),
)
tempZipStorage += ".zip"
websocket_helper.broadcast_user(exec_user["user_id"], "send_logs_bootbox", {})
temp_zip_storage += ".zip"
self.helper.websocket_helper.broadcast_user(
exec_user["user_id"], "send_logs_bootbox", {}
)
self.users.set_support_path(exec_user["user_id"], tempZipStorage)
self.users.set_support_path(exec_user["user_id"], temp_zip_storage)
self.users.stop_prepare(exec_user["user_id"])
self.support_scheduler.remove_job("logs_" + str(exec_user["user_id"]))
@staticmethod
def add_system_user():
helper_users.add_user(
def add_system_user(self):
self.users_helper.add_user(
"system",
helper.random_string_generator(64),
Helpers.random_string_generator(64),
"default@example.com",
False,
False,
)
def get_server_settings(self, server_id):
for s in self.servers_list:
if int(s["server_id"]) == int(server_id):
return s["server_settings"]
for server in self.servers_list:
if int(server["server_id"]) == int(server_id):
return server["server_settings"]
logger.warning(f"Unable to find server object for server id {server_id}")
return False
@ -254,11 +262,11 @@ class Controller:
svr.stop_crash_detection()
def log_status(self, source_path, dest_path, exec_user):
results = helper.calc_percent(source_path, dest_path)
results = Helpers.calc_percent(source_path, dest_path)
self.log_stats = results
if len(websocket_helper.clients) > 0:
websocket_helper.broadcast_user(
if len(self.helper.websocket_helper.clients) > 0:
self.helper.websocket_helper.broadcast_user(
exec_user["user_id"], "support_status_update", results
)
@ -269,34 +277,34 @@ class Controller:
return {"percent": 0, "total_files": 0}
def get_server_obj(self, server_id: Union[str, int]) -> Union[bool, Server]:
for s in self.servers_list:
if str(s["server_id"]) == str(server_id):
return s["server_obj"]
for server in self.servers_list:
if str(server["server_id"]) == str(server_id):
return server["server_obj"]
logger.warning(f"Unable to find server object for server id {server_id}")
return False # TODO: Change to None
def get_server_data(self, server_id: str):
for s in self.servers_list:
if str(s["server_id"]) == str(server_id):
return s["server_data_obj"]
for server in self.servers_list:
if str(server["server_id"]) == str(server_id):
return server["server_data_obj"]
logger.warning(f"Unable to find server object for server id {server_id}")
return False
@staticmethod
def list_defined_servers():
servers = servers_helper.get_all_defined_servers()
servers = HelperServers.get_all_defined_servers()
return servers
def list_running_servers(self):
running_servers = []
# for each server
for s in self.servers_list:
for servers in self.servers_list:
# is the server running?
srv_obj = s["server_obj"]
srv_obj = servers["server_obj"]
running = srv_obj.check_running()
# if so, let's add a dictionary to the list of running servers
if running:
@ -307,22 +315,22 @@ class Controller:
def stop_all_servers(self):
servers = self.list_running_servers()
logger.info(f"Found {len(servers)} running server(s)")
console.info(f"Found {len(servers)} running server(s)")
Console.info(f"Found {len(servers)} running server(s)")
logger.info("Stopping All Servers")
console.info("Stopping All Servers")
Console.info("Stopping All Servers")
for s in servers:
logger.info(f"Stopping Server ID {s['id']} - {s['name']}")
console.info(f"Stopping Server ID {s['id']} - {s['name']}")
for server in servers:
logger.info(f"Stopping Server ID {server['id']} - {server['name']}")
Console.info(f"Stopping Server ID {server['id']} - {server['name']}")
self.stop_server(s["id"])
self.stop_server(server["id"])
# let's wait 2 seconds to let everything flush out
time.sleep(2)
logger.info("All Servers Stopped")
console.info("All Servers Stopped")
Console.info("All Servers Stopped")
def stop_server(self, server_id):
# issue the stop command
@ -338,12 +346,12 @@ class Controller:
max_mem: int,
port: int,
):
server_id = helper.create_uuid()
server_dir = os.path.join(helper.servers_dir, server_id)
backup_path = os.path.join(helper.backup_path, server_id)
if helper.is_os_windows():
server_dir = helper.wtol_path(server_dir)
backup_path = helper.wtol_path(backup_path)
server_id = Helpers.create_uuid()
server_dir = os.path.join(self.helper.servers_dir, server_id)
backup_path = os.path.join(self.helper.backup_path, server_id)
if Helpers.is_os_windows():
server_dir = Helpers.wtol_path(server_dir)
backup_path = Helpers.wtol_path(backup_path)
server_dir.replace(" ", "^ ")
backup_path.replace(" ", "^ ")
@ -351,36 +359,38 @@ class Controller:
full_jar_path = os.path.join(server_dir, server_file)
# make the dir - perhaps a UUID?
helper.ensure_dir_exists(server_dir)
helper.ensure_dir_exists(backup_path)
Helpers.ensure_dir_exists(server_dir)
Helpers.ensure_dir_exists(backup_path)
try:
# do a eula.txt
with open(os.path.join(server_dir, "eula.txt"), "w", encoding="utf-8") as f:
f.write("eula=false")
f.close()
with open(
os.path.join(server_dir, "eula.txt"), "w", encoding="utf-8"
) as file:
file.write("eula=false")
file.close()
# setup server.properties with the port
with open(
os.path.join(server_dir, "server.properties"), "w", encoding="utf-8"
) as f:
f.write(f"server-port={port}")
f.close()
) as file:
file.write(f"server-port={port}")
file.close()
except Exception as e:
logger.error(f"Unable to create required server files due to :{e}")
return False
if helper.is_os_windows():
if Helpers.is_os_windows():
server_command = (
f"java -Xms{helper.float_to_string(min_mem)}M "
f"-Xmx{helper.float_to_string(max_mem)}M "
f"java -Xms{Helpers.float_to_string(min_mem)}M "
f"-Xmx{Helpers.float_to_string(max_mem)}M "
f'-jar "{full_jar_path}" nogui'
)
else:
server_command = (
f"java -Xms{helper.float_to_string(min_mem)}M "
f"-Xmx{helper.float_to_string(max_mem)}M "
f"java -Xms{Helpers.float_to_string(min_mem)}M "
f"-Xmx{Helpers.float_to_string(max_mem)}M "
f"-jar {full_jar_path} nogui"
)
server_log_file = f"{server_dir}/logs/latest.log"
@ -400,23 +410,23 @@ class Controller:
)
# download the jar
server_jar_obj.download_jar(server, version, full_jar_path, new_id)
self.server_jars.download_jar(server, version, full_jar_path, new_id)
return new_id
@staticmethod
def verify_jar_server(server_path: str, server_jar: str):
server_path = helper.get_os_understandable_path(server_path)
path_check = helper.check_path_exists(server_path)
jar_check = helper.check_file_exists(os.path.join(server_path, server_jar))
server_path = Helpers.get_os_understandable_path(server_path)
path_check = Helpers.check_path_exists(server_path)
jar_check = Helpers.check_file_exists(os.path.join(server_path, server_jar))
if not path_check or not jar_check:
return False
return True
@staticmethod
def verify_zip_server(zip_path: str):
zip_path = helper.get_os_understandable_path(zip_path)
zip_check = helper.check_file_exists(zip_path)
zip_path = Helpers.get_os_understandable_path(zip_path)
zip_check = Helpers.check_file_exists(zip_path)
if not zip_check:
return False
return True
@ -430,20 +440,20 @@ class Controller:
max_mem: int,
port: int,
):
server_id = helper.create_uuid()
new_server_dir = os.path.join(helper.servers_dir, server_id)
backup_path = os.path.join(helper.backup_path, server_id)
if helper.is_os_windows():
new_server_dir = helper.wtol_path(new_server_dir)
backup_path = helper.wtol_path(backup_path)
server_id = Helpers.create_uuid()
new_server_dir = os.path.join(self.helper.servers_dir, server_id)
backup_path = os.path.join(self.helper.backup_path, server_id)
if Helpers.is_os_windows():
new_server_dir = Helpers.wtol_path(new_server_dir)
backup_path = Helpers.wtol_path(backup_path)
new_server_dir.replace(" ", "^ ")
backup_path.replace(" ", "^ ")
helper.ensure_dir_exists(new_server_dir)
helper.ensure_dir_exists(backup_path)
server_path = helper.get_os_understandable_path(server_path)
Helpers.ensure_dir_exists(new_server_dir)
Helpers.ensure_dir_exists(backup_path)
server_path = Helpers.get_os_understandable_path(server_path)
try:
file_helper.copy_dir(server_path, new_server_dir, True)
FileHelpers.copy_dir(server_path, new_server_dir, True)
except shutil.Error as ex:
logger.error(f"Server import failed with error: {ex}")
@ -458,22 +468,22 @@ class Controller:
)
with open(
os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8"
) as f:
f.write(f"server-port={port}")
f.close()
) as file:
file.write(f"server-port={port}")
file.close()
full_jar_path = os.path.join(new_server_dir, server_jar)
if helper.is_os_windows():
if Helpers.is_os_windows():
server_command = (
f"java -Xms{helper.float_to_string(min_mem)}M "
f"-Xmx{helper.float_to_string(max_mem)}M "
f"java -Xms{Helpers.float_to_string(min_mem)}M "
f"-Xmx{Helpers.float_to_string(max_mem)}M "
f'-jar "{full_jar_path}" nogui'
)
else:
server_command = (
f"java -Xms{helper.float_to_string(min_mem)}M "
f"-Xmx{helper.float_to_string(max_mem)}M "
f"java -Xms{Helpers.float_to_string(min_mem)}M "
f"-Xmx{Helpers.float_to_string(max_mem)}M "
f"-jar {full_jar_path} nogui"
)
server_log_file = f"{new_server_dir}/logs/latest.log"
@ -502,31 +512,31 @@ class Controller:
max_mem: int,
port: int,
):
server_id = helper.create_uuid()
new_server_dir = os.path.join(helper.servers_dir, server_id)
backup_path = os.path.join(helper.backup_path, server_id)
if helper.is_os_windows():
new_server_dir = helper.wtol_path(new_server_dir)
backup_path = helper.wtol_path(backup_path)
server_id = Helpers.create_uuid()
new_server_dir = os.path.join(self.helper.servers_dir, server_id)
backup_path = os.path.join(self.helper.backup_path, server_id)
if Helpers.is_os_windows():
new_server_dir = Helpers.wtol_path(new_server_dir)
backup_path = Helpers.wtol_path(backup_path)
new_server_dir.replace(" ", "^ ")
backup_path.replace(" ", "^ ")
tempDir = helper.get_os_understandable_path(zip_path)
helper.ensure_dir_exists(new_server_dir)
helper.ensure_dir_exists(backup_path)
temp_dir = Helpers.get_os_understandable_path(zip_path)
Helpers.ensure_dir_exists(new_server_dir)
Helpers.ensure_dir_exists(backup_path)
has_properties = False
# extracts archive to temp directory
for item in os.listdir(tempDir):
for item in os.listdir(temp_dir):
if str(item) == "server.properties":
has_properties = True
try:
if not os.path.isdir(os.path.join(tempDir, item)):
file_helper.move_file(
os.path.join(tempDir, item), os.path.join(new_server_dir, item)
if not os.path.isdir(os.path.join(temp_dir, item)):
FileHelpers.move_file(
os.path.join(temp_dir, item), os.path.join(new_server_dir, item)
)
else:
file_helper.move_dir(
os.path.join(tempDir, item), os.path.join(new_server_dir, item)
FileHelpers.move_dir(
os.path.join(temp_dir, item), os.path.join(new_server_dir, item)
)
except Exception as ex:
logger.error(f"ERROR IN ZIP IMPORT: {ex}")
@ -537,22 +547,22 @@ class Controller:
)
with open(
os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8"
) as f:
f.write(f"server-port={port}")
f.close()
) as file:
file.write(f"server-port={port}")
file.close()
full_jar_path = os.path.join(new_server_dir, server_jar)
if helper.is_os_windows():
if Helpers.is_os_windows():
server_command = (
f"java -Xms{helper.float_to_string(min_mem)}M "
f"-Xmx{helper.float_to_string(max_mem)}M "
f"java -Xms{Helpers.float_to_string(min_mem)}M "
f"-Xmx{Helpers.float_to_string(max_mem)}M "
f'-jar "{full_jar_path}" nogui'
)
else:
server_command = (
f"java -Xms{helper.float_to_string(min_mem)}M "
f"-Xmx{helper.float_to_string(max_mem)}M "
f"java -Xms{Helpers.float_to_string(min_mem)}M "
f"-Xmx{Helpers.float_to_string(max_mem)}M "
f"-jar {full_jar_path} nogui"
)
logger.debug("command: " + server_command)
@ -580,20 +590,20 @@ class Controller:
def import_bedrock_server(
self, server_name: str, server_path: str, server_exe: str, port: int
):
server_id = helper.create_uuid()
new_server_dir = os.path.join(helper.servers_dir, server_id)
backup_path = os.path.join(helper.backup_path, server_id)
if helper.is_os_windows():
new_server_dir = helper.wtol_path(new_server_dir)
backup_path = helper.wtol_path(backup_path)
server_id = Helpers.create_uuid()
new_server_dir = os.path.join(self.helper.servers_dir, server_id)
backup_path = os.path.join(self.helper.backup_path, server_id)
if Helpers.is_os_windows():
new_server_dir = Helpers.wtol_path(new_server_dir)
backup_path = Helpers.wtol_path(backup_path)
new_server_dir.replace(" ", "^ ")
backup_path.replace(" ", "^ ")
helper.ensure_dir_exists(new_server_dir)
helper.ensure_dir_exists(backup_path)
server_path = helper.get_os_understandable_path(server_path)
Helpers.ensure_dir_exists(new_server_dir)
Helpers.ensure_dir_exists(backup_path)
server_path = Helpers.get_os_understandable_path(server_path)
try:
file_helper.copy_dir(server_path, new_server_dir, True)
FileHelpers.copy_dir(server_path, new_server_dir, True)
except shutil.Error as ex:
logger.error(f"Server import failed with error: {ex}")
@ -608,13 +618,13 @@ class Controller:
)
with open(
os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8"
) as f:
f.write(f"server-port={port}")
f.close()
) as file:
file.write(f"server-port={port}")
file.close()
full_jar_path = os.path.join(new_server_dir, server_exe)
if helper.is_os_windows():
if Helpers.is_os_windows():
server_command = f'"{full_jar_path}"'
else:
server_command = f"./{server_exe}"
@ -635,38 +645,38 @@ class Controller:
server_type="minecraft-bedrock",
)
if os.name != "nt":
if helper.check_file_exists(full_jar_path):
if Helpers.check_file_exists(full_jar_path):
os.chmod(full_jar_path, 0o2775)
return new_id
def import_bedrock_zip_server(
self, server_name: str, zip_path: str, server_exe: str, port: int
):
server_id = helper.create_uuid()
new_server_dir = os.path.join(helper.servers_dir, server_id)
backup_path = os.path.join(helper.backup_path, server_id)
if helper.is_os_windows():
new_server_dir = helper.wtol_path(new_server_dir)
backup_path = helper.wtol_path(backup_path)
server_id = Helpers.create_uuid()
new_server_dir = os.path.join(self.helper.servers_dir, server_id)
backup_path = os.path.join(self.helper.backup_path, server_id)
if Helpers.is_os_windows():
new_server_dir = Helpers.wtol_path(new_server_dir)
backup_path = Helpers.wtol_path(backup_path)
new_server_dir.replace(" ", "^ ")
backup_path.replace(" ", "^ ")
tempDir = helper.get_os_understandable_path(zip_path)
helper.ensure_dir_exists(new_server_dir)
helper.ensure_dir_exists(backup_path)
temp_dir = Helpers.get_os_understandable_path(zip_path)
Helpers.ensure_dir_exists(new_server_dir)
Helpers.ensure_dir_exists(backup_path)
has_properties = False
# extracts archive to temp directory
for item in os.listdir(tempDir):
for item in os.listdir(temp_dir):
if str(item) == "server.properties":
has_properties = True
try:
if not os.path.isdir(os.path.join(tempDir, item)):
file_helper.move_file(
os.path.join(tempDir, item), os.path.join(new_server_dir, item)
if not os.path.isdir(os.path.join(temp_dir, item)):
FileHelpers.move_file(
os.path.join(temp_dir, item), os.path.join(new_server_dir, item)
)
else:
file_helper.move_dir(
os.path.join(tempDir, item), os.path.join(new_server_dir, item)
FileHelpers.move_dir(
os.path.join(temp_dir, item), os.path.join(new_server_dir, item)
)
except Exception as ex:
logger.error(f"ERROR IN ZIP IMPORT: {ex}")
@ -677,13 +687,13 @@ class Controller:
)
with open(
os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8"
) as f:
f.write(f"server-port={port}")
f.close()
) as file:
file.write(f"server-port={port}")
file.close()
full_jar_path = os.path.join(new_server_dir, server_exe)
if helper.is_os_windows():
if Helpers.is_os_windows():
server_command = f'"{full_jar_path}"'
else:
server_command = f"./{server_exe}"
@ -704,7 +714,7 @@ class Controller:
server_type="minecraft-bedrock",
)
if os.name != "nt":
if helper.check_file_exists(full_jar_path):
if Helpers.check_file_exists(full_jar_path):
os.chmod(full_jar_path, 0o2775)
return new_id
@ -716,21 +726,24 @@ class Controller:
def rename_backup_dir(self, old_server_id, new_server_id, new_uuid):
server_data = self.servers.get_server_data_by_id(old_server_id)
old_bu_path = server_data["backup_path"]
Server_Perms_Controller.backup_role_swap(old_server_id, new_server_id)
if not helper.is_os_windows():
backup_path = helper.validate_traversal(helper.backup_path, old_bu_path)
if helper.is_os_windows():
backup_path = helper.validate_traversal(
helper.wtol_path(helper.backup_path), helper.wtol_path(old_bu_path)
ServerPermsController.backup_role_swap(old_server_id, new_server_id)
if not Helpers.is_os_windows():
backup_path = Helpers.validate_traversal(
self.helper.backup_path, old_bu_path
)
backup_path = helper.wtol_path(str(backup_path))
if Helpers.is_os_windows():
backup_path = Helpers.validate_traversal(
Helpers.wtol_path(self.helper.backup_path),
Helpers.wtol_path(old_bu_path),
)
backup_path = Helpers.wtol_path(str(backup_path))
backup_path.replace(" ", "^ ")
backup_path = Path(backup_path)
backup_path_components = list(backup_path.parts)
backup_path_components[-1] = new_uuid
new_bu_path = pathlib.PurePath(os.path.join(*backup_path_components))
if os.path.isdir(new_bu_path):
if helper.validate_traversal(helper.backup_path, new_bu_path):
if Helpers.validate_traversal(self.helper.backup_path, new_bu_path):
os.rmdir(new_bu_path)
backup_path.rename(new_bu_path)
@ -748,7 +761,6 @@ class Controller:
server_type: str,
):
# put data in the db
new_id = self.servers.create_server(
name,
server_uuid,
@ -762,19 +774,21 @@ class Controller:
server_port,
)
if not helper.check_file_exists(os.path.join(server_dir, "crafty_managed.txt")):
if not Helpers.check_file_exists(
os.path.join(server_dir, "crafty_managed.txt")
):
try:
# place a file in the dir saying it's owned by crafty
with open(
os.path.join(server_dir, "crafty_managed.txt"),
"w",
encoding="utf-8",
) as f:
f.write(
) as file:
file.write(
"The server is managed by Crafty Controller.\n "
"Leave this directory/files alone please"
)
f.close()
file.close()
except Exception as e:
logger.error(f"Unable to create required server files due to :{e}")
@ -787,25 +801,26 @@ class Controller:
def remove_server(self, server_id, files):
counter = 0
for s in self.servers_list:
for server in self.servers_list:
# if this is the droid... im mean server we are looking for...
if str(s["server_id"]) == str(server_id):
if str(server["server_id"]) == str(server_id):
server_data = self.get_server_data(server_id)
server_name = server_data["server_name"]
logger.info(f"Deleting Server: ID {server_id} | Name: {server_name} ")
console.info(f"Deleting Server: ID {server_id} | Name: {server_name} ")
Console.info(f"Deleting Server: ID {server_id} | Name: {server_name} ")
srv_obj = s["server_obj"]
srv_obj = server["server_obj"]
srv_obj.server_scheduler.shutdown()
running = srv_obj.check_running()
if running:
self.stop_server(server_id)
if files:
try:
file_helper.del_dirs(
helper.get_os_understandable_path(
FileHelpers.del_dirs(
Helpers.get_os_understandable_path(
self.servers.get_server_data_by_id(server_id)["path"]
)
)
@ -814,11 +829,11 @@ class Controller:
f"Unable to delete server files for server with ID: "
f"{server_id} with error logged: {e}"
)
if helper.check_path_exists(
if Helpers.check_path_exists(
self.servers.get_server_data_by_id(server_id)["backup_path"]
):
file_helper.del_dirs(
helper.get_os_understandable_path(
FileHelpers.del_dirs(
Helpers.get_os_understandable_path(
self.servers.get_server_data_by_id(server_id)[
"backup_path"
]
@ -827,7 +842,7 @@ class Controller:
# Cleanup scheduled tasks
try:
helpers_management.delete_scheduled_task_by_server(server_id)
HelpersManagement.delete_scheduled_task_by_server(server_id)
except DoesNotExist:
logger.info("No scheduled jobs exist. Continuing.")
# remove the server from the DB
@ -840,8 +855,8 @@ class Controller:
@staticmethod
def clear_unexecuted_commands():
helpers_management.clear_unexecuted_commands()
HelpersManagement.clear_unexecuted_commands()
@staticmethod
def clear_support_status():
helper_users.clear_support_status()
HelperUsers.clear_support_status()

View File

@ -1,58 +1,43 @@
import logging
from playhouse.shortcuts import model_to_dict
from app.classes.models.users import Users, users_helper
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
# To disable warning about unused import ; Users is imported from here in other places
# pylint: disable=self-assigning-variable
Users = Users
try:
# pylint: disable=unused-import
from peewee import SqliteDatabase, fn
from playhouse.shortcuts import model_to_dict
except ModuleNotFoundError as err:
helper.auto_installer_fix(err)
from app.classes.shared.helpers import Helpers # pylint: disable=unused-import
from app.classes.shared.console import Console
logger = logging.getLogger(__name__)
peewee_logger = logging.getLogger("peewee")
peewee_logger.setLevel(logging.INFO)
database = SqliteDatabase(
helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
)
class db_builder:
@staticmethod
def default_settings():
class DatabaseBuilder:
def __init__(self, database, helper, users_helper):
self.database = database
self.helper = helper
self.users_helper = users_helper
def default_settings(self):
logger.info("Fresh Install Detected - Creating Default Settings")
console.info("Fresh Install Detected - Creating Default Settings")
default_data = helper.find_default_password()
Console.info("Fresh Install Detected - Creating Default Settings")
default_data = self.helper.find_default_password()
username = default_data.get("username", "admin")
password = default_data.get("password", "crafty")
users_helper.add_user(
self.users_helper.add_user(
username=username,
password=password,
email="default@example.com",
superuser=True,
)
@staticmethod
def is_fresh_install():
def is_fresh_install(self):
try:
user = users_helper.get_by_id(1)
user = self.users_helper.get_by_id(1)
if user:
return False
except:
return True
class db_shortcuts:
class DatabaseShortcuts:
# **********************************************************************************
# Generic Databse Methods
# **********************************************************************************
@ -73,10 +58,3 @@ class db_shortcuts:
def return_db_rows(model):
data = [model_to_dict(row) for row in model]
return data
# **********************************************************************************
# Static Accessors
# **********************************************************************************
installer = db_builder()
db_helper = db_shortcuts()

View File

@ -1,481 +1,483 @@
# pylint: skip-file
from datetime import datetime
import logging
import typing as t
import sys
import os
import re
from functools import wraps
from functools import cached_property
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
try:
import peewee
from playhouse.migrate import (
SqliteMigrator,
Operation,
SQL,
SqliteDatabase,
make_index_name,
)
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
logger = logging.getLogger(__name__)
MIGRATE_TABLE = "migratehistory"
MIGRATE_TEMPLATE = '''# Generated by database migrator
import peewee
def migrate(migrator, db):
"""
Write your migrations here.
"""
{migrate}
def rollback(migrator, db):
"""
Write your rollback migrations here.
"""
{rollback}'''
class MigrateHistory(peewee.Model):
"""
Presents the migration history in a database.
"""
name = peewee.CharField(unique=True)
migrated_at = peewee.DateTimeField(default=datetime.utcnow)
# noinspection PyTypeChecker
def __unicode__(self) -> str:
"""
String representation of this migration
"""
return self.name
class Meta:
table_name = MIGRATE_TABLE
def get_model(method):
"""
Convert string to model class.
"""
@wraps(method)
def wrapper(migrator, model, *args, **kwargs):
if isinstance(model, str):
return method(migrator, migrator.table_dict[model], *args, **kwargs)
return method(migrator, model, *args, **kwargs)
return wrapper
# noinspection PyProtectedMember
class Migrator(object):
def __init__(self, database: t.Union[peewee.Database, peewee.Proxy]):
"""
Initializes the migrator
"""
if isinstance(database, peewee.Proxy):
database = database.obj
self.database: SqliteDatabase = database
self.table_dict: t.Dict[str, peewee.Model] = {}
self.operations: t.List[t.Union[Operation, callable]] = []
self.migrator = SqliteMigrator(database)
def run(self):
"""
Runs operations.
"""
for op in self.operations:
if isinstance(op, Operation):
op.run()
else:
op()
self.clean()
def clean(self):
"""
Cleans the operations.
"""
self.operations = list()
def sql(self, sql: str, *params):
"""
Executes raw SQL.
"""
self.operations.append(SQL(sql, *params))
def create_table(self, model: peewee.Model) -> peewee.Model:
"""
Creates model and table in database.
"""
self.table_dict[model._meta.table_name] = model
model._meta.database = self.database
self.operations.append(model.create_table)
return model
@get_model
def drop_table(self, model: peewee.Model):
"""
Drops model and table from database.
"""
del self.table_dict[model._meta.table_name]
self.operations.append(lambda: model.drop_table(cascade=False))
@get_model
def add_columns(self, model: peewee.Model, **fields: peewee.Field) -> peewee.Model:
"""
Creates new fields.
"""
for name, field in fields.items():
model._meta.add_field(name, field)
self.operations.append(
self.migrator.add_column(
model._meta.table_name, field.column_name, field
)
)
if field.unique:
self.operations.append(
self.migrator.add_index(
model._meta.table_name, (field.column_name,), unique=True
)
)
return model
@get_model
def drop_columns(self, model: peewee.Model, names: str) -> peewee.Model:
"""
Removes fields from model.
"""
fields = [field for field in model._meta.fields.values() if field.name in names]
for field in fields:
self.__del_field__(model, field)
if field.unique:
# Drop unique index
index_name = make_index_name(
model._meta.table_name, [field.column_name]
)
self.operations.append(
self.migrator.drop_index(model._meta.table_name, index_name)
)
self.operations.append(
self.migrator.drop_column(
model._meta.table_name, field.column_name, cascade=False
)
)
return model
def __del_field__(self, model: peewee.Model, field: peewee.Field):
"""
Deletes field from model.
"""
model._meta.remove_field(field.name)
delattr(model, field.name)
if isinstance(field, peewee.ForeignKeyField):
obj_id_name = field.column_name
if field.column_name == field.name:
obj_id_name += "_id"
delattr(model, obj_id_name)
delattr(field.rel_model, field.backref)
@get_model
def rename_column(
self, model: peewee.Model, old_name: str, new_name: str
) -> peewee.Model:
"""
Renames field in model.
"""
field = model._meta.fields[old_name]
if isinstance(field, peewee.ForeignKeyField):
old_name = field.column_name
self.__del_field__(model, field)
field.name = field.column_name = new_name
model._meta.add_field(new_name, field)
if isinstance(field, peewee.ForeignKeyField):
field.column_name = new_name = field.column_name + "_id"
self.operations.append(
self.migrator.rename_column(model._meta.table_name, old_name, new_name)
)
return model
@get_model
def rename_table(self, model: peewee.Model, new_name: str) -> peewee.Model:
"""
Renames table in database.
"""
old_name = model._meta.table_name
del self.table_dict[model._meta.table_name]
model._meta.table_name = new_name
self.table_dict[model._meta.table_name] = model
self.operations.append(self.migrator.rename_table(old_name, new_name))
return model
@get_model
def add_index(
self, model: peewee.Model, *columns: str, unique=False
) -> peewee.Model:
"""Create indexes."""
model._meta.indexes.append((columns, unique))
columns_ = []
for col in columns:
field = model._meta.fields.get(col)
if len(columns) == 1:
field.unique = unique
field.index = not unique
if isinstance(field, peewee.ForeignKeyField):
col = col + "_id"
columns_.append(col)
self.operations.append(
self.migrator.add_index(model._meta.table_name, columns_, unique=unique)
)
return model
@get_model
def drop_index(self, model: peewee.Model, *columns: str) -> peewee.Model:
"""Drop indexes."""
columns_ = []
for col in columns:
field = model._meta.fields.get(col)
if not field:
continue
if len(columns) == 1:
field.unique = field.index = False
if isinstance(field, peewee.ForeignKeyField):
col = col + "_id"
columns_.append(col)
index_name = make_index_name(model._meta.table_name, columns_)
model._meta.indexes = [
(cols, _) for (cols, _) in model._meta.indexes if columns != cols
]
self.operations.append(
self.migrator.drop_index(model._meta.table_name, index_name)
)
return model
@get_model
def add_not_null(self, model: peewee.Model, *names: str) -> peewee.Model:
"""Add not null."""
for name in names:
field = model._meta.fields[name]
field.null = False
self.operations.append(
self.migrator.add_not_null(model._meta.table_name, field.column_name)
)
return model
@get_model
def drop_not_null(self, model: peewee.Model, *names: str) -> peewee.Model:
"""Drop not null."""
for name in names:
field = model._meta.fields[name]
field.null = True
self.operations.append(
self.migrator.drop_not_null(model._meta.table_name, field.column_name)
)
return model
@get_model
def add_default(
self, model: peewee.Model, name: str, default: t.Any
) -> peewee.Model:
"""Add default."""
field = model._meta.fields[name]
model._meta.defaults[field] = field.default = default
self.operations.append(
self.migrator.apply_default(model._meta.table_name, name, field)
)
return model
# noinspection PyProtectedMember
class MigrationManager(object):
filemask = re.compile(r"[\d]+_[^\.]+\.py$")
def __init__(self, database: t.Union[peewee.Database, peewee.Proxy]):
"""
Initializes the migration manager.
"""
if not isinstance(database, (peewee.Database, peewee.Proxy)):
raise RuntimeError("Invalid database: {}".format(database))
self.database = database
@cached_property
def model(self) -> t.Type[MigrateHistory]:
"""
Initialize and cache the MigrationHistory model.
"""
MigrateHistory._meta.database = self.database
MigrateHistory._meta.table_name = "migratehistory"
MigrateHistory._meta.schema = None
MigrateHistory.create_table(True)
return MigrateHistory
@property
def done(self) -> t.List[str]:
"""
Scans migrations in the database.
"""
return [mm.name for mm in self.model.select().order_by(self.model.id)]
@property
def todo(self):
"""
Scans migrations in the file system.
"""
if not os.path.exists(helper.migration_dir):
logger.warning(
"Migration directory: {} does not exist.".format(helper.migration_dir)
)
os.makedirs(helper.migration_dir)
return sorted(
f[:-3] for f in os.listdir(helper.migration_dir) if self.filemask.match(f)
)
@property
def diff(self) -> t.List[str]:
"""
Calculates difference between the filesystem and the database.
"""
done = set(self.done)
return [name for name in self.todo if name not in done]
@cached_property
def migrator(self) -> Migrator:
"""
Create migrator and setup it with fake migrations.
"""
migrator = Migrator(self.database)
for name in self.done:
self.up_one(name, migrator, True)
return migrator
def compile(self, name, migrate="", rollback=""):
"""
Compiles a migration.
"""
name = datetime.utcnow().strftime("%Y%m%d%H%M%S") + "_" + name
filename = name + ".py"
path = os.path.join(helper.migration_dir, filename)
with open(path, "w") as f:
f.write(
MIGRATE_TEMPLATE.format(
migrate=migrate, rollback=rollback, name=filename
)
)
return name
def create(self, name: str = "auto", auto: bool = False) -> t.Optional[str]:
"""
Creates a migration.
"""
migrate = rollback = ""
if auto:
raise NotImplementedError
logger.info('Creating migration "{}"'.format(name))
name = self.compile(name, migrate, rollback)
logger.info('Migration has been created as "{}"'.format(name))
return name
def clear(self):
"""Clear migrations."""
self.model.delete().execute()
def up(self, name: t.Optional[str] = None):
"""
Runs all unapplied migrations.
"""
logger.info("Starting migrations")
console.info("Starting migrations")
done = []
diff = self.diff
if not diff:
logger.info("There is nothing to migrate")
console.info("There is nothing to migrate")
return done
migrator = self.migrator
for mname in diff:
done.append(self.up_one(mname, self.migrator))
if name and name == mname:
break
return done
def read(self, name: str):
"""
Reads a migration from a file.
"""
call_params = dict()
if helper.is_os_windows() and sys.version_info >= (3, 0):
# if system is windows - force utf-8 encoding
call_params["encoding"] = "utf-8"
with open(os.path.join(helper.migration_dir, name + ".py"), **call_params) as f:
code = f.read()
scope = {}
code = compile(code, "<string>", "exec", dont_inherit=True)
exec(code, scope, None)
return scope.get("migrate", lambda m, d: None), scope.get(
"rollback", lambda m, d: None
)
def up_one(
self, name: str, migrator: Migrator, fake: bool = False, rollback: bool = False
) -> str:
"""
Runs a migration with a given name.
"""
try:
migrate_fn, rollback_fn = self.read(name)
if fake:
migrate_fn(migrator, self.database)
migrator.clean()
return name
with self.database.transaction():
if rollback:
logger.info('Rolling back "{}"'.format(name))
rollback_fn(migrator, self.database)
migrator.run()
self.model.delete().where(self.model.name == name).execute()
else:
logger.info('Migrate "{}"'.format(name))
migrate_fn(migrator, self.database)
migrator.run()
if name not in self.done:
self.model.create(name=name)
logger.info('Done "{}"'.format(name))
return name
except Exception:
self.database.rollback()
operation_name = "Rollback" if rollback else "Migration"
logger.exception("{} failed: {}".format(operation_name, name))
raise
def down(self):
"""
Rolls back migrations.
"""
if not self.done:
raise RuntimeError("No migrations are found.")
name = self.done[-1]
migrator = self.migrator
self.up_one(name, migrator, False, True)
logger.warning("Rolled back migration: {}".format(name))
# pylint: skip-file
from datetime import datetime
import logging
import typing as t
import sys
import os
import re
from functools import wraps
from functools import cached_property
import peewee
from playhouse.migrate import (
SqliteMigrator,
Operation,
SQL,
SqliteDatabase,
make_index_name,
)
from app.classes.shared.console import Console
from app.classes.shared.helpers import Helpers
logger = logging.getLogger(__name__)
MIGRATE_TABLE = "migratehistory"
MIGRATE_TEMPLATE = '''# Generated by database migrator
import peewee
def migrate(migrator, db):
"""
Write your migrations here.
"""
{migrate}
def rollback(migrator, db):
"""
Write your rollback migrations here.
"""
{rollback}'''
class MigrateHistory(peewee.Model):
"""
Presents the migration history in a database.
"""
name = peewee.CharField(unique=True)
migrated_at = peewee.DateTimeField(default=datetime.utcnow)
# noinspection PyTypeChecker
def __unicode__(self) -> str:
"""
String representation of this migration
"""
return self.name
class Meta:
table_name = MIGRATE_TABLE
def get_model(method):
"""
Convert string to model class.
"""
@wraps(method)
def wrapper(migrator, model, *args, **kwargs):
if isinstance(model, str):
return method(migrator, migrator.table_dict[model], *args, **kwargs)
return method(migrator, model, *args, **kwargs)
return wrapper
# noinspection PyProtectedMember
class Migrator(object):
def __init__(self, database: t.Union[peewee.Database, peewee.Proxy]):
"""
Initializes the migrator
"""
if isinstance(database, peewee.Proxy):
database = database.obj
self.database: SqliteDatabase = database
self.table_dict: t.Dict[str, peewee.Model] = {}
self.operations: t.List[t.Union[Operation, callable]] = []
self.migrator = SqliteMigrator(database)
def run(self):
"""
Runs operations.
"""
for op in self.operations:
if isinstance(op, Operation):
op.run()
else:
op()
self.clean()
def clean(self):
"""
Cleans the operations.
"""
self.operations = list()
def sql(self, sql: str, *params):
"""
Executes raw SQL.
"""
self.operations.append(SQL(sql, *params))
def create_table(self, model: peewee.Model) -> peewee.Model:
"""
Creates model and table in database.
"""
self.table_dict[model._meta.table_name] = model
model._meta.database = self.database
self.operations.append(model.create_table)
return model
@get_model
def drop_table(self, model: peewee.Model):
"""
Drops model and table from database.
"""
del self.table_dict[model._meta.table_name]
self.operations.append(lambda: model.drop_table(cascade=False))
@get_model
def add_columns(self, model: peewee.Model, **fields: peewee.Field) -> peewee.Model:
"""
Creates new fields.
"""
for name, field in fields.items():
model._meta.add_field(name, field)
self.operations.append(
self.migrator.add_column(
model._meta.table_name, field.column_name, field
)
)
if field.unique:
self.operations.append(
self.migrator.add_index(
model._meta.table_name, (field.column_name,), unique=True
)
)
return model
@get_model
def drop_columns(self, model: peewee.Model, names: str) -> peewee.Model:
"""
Removes fields from model.
"""
fields = [field for field in model._meta.fields.values() if field.name in names]
for field in fields:
self.__del_field__(model, field)
if field.unique:
# Drop unique index
index_name = make_index_name(
model._meta.table_name, [field.column_name]
)
self.operations.append(
self.migrator.drop_index(model._meta.table_name, index_name)
)
self.operations.append(
self.migrator.drop_column(
model._meta.table_name, field.column_name, cascade=False
)
)
return model
def __del_field__(self, model: peewee.Model, field: peewee.Field):
"""
Deletes field from model.
"""
model._meta.remove_field(field.name)
delattr(model, field.name)
if isinstance(field, peewee.ForeignKeyField):
obj_id_name = field.column_name
if field.column_name == field.name:
obj_id_name += "_id"
delattr(model, obj_id_name)
delattr(field.rel_model, field.backref)
@get_model
def rename_column(
self, model: peewee.Model, old_name: str, new_name: str
) -> peewee.Model:
"""
Renames field in model.
"""
field = model._meta.fields[old_name]
if isinstance(field, peewee.ForeignKeyField):
old_name = field.column_name
self.__del_field__(model, field)
field.name = field.column_name = new_name
model._meta.add_field(new_name, field)
if isinstance(field, peewee.ForeignKeyField):
field.column_name = new_name = field.column_name + "_id"
self.operations.append(
self.migrator.rename_column(model._meta.table_name, old_name, new_name)
)
return model
@get_model
def rename_table(self, model: peewee.Model, new_name: str) -> peewee.Model:
"""
Renames table in database.
"""
old_name = model._meta.table_name
del self.table_dict[model._meta.table_name]
model._meta.table_name = new_name
self.table_dict[model._meta.table_name] = model
self.operations.append(self.migrator.rename_table(old_name, new_name))
return model
@get_model
def add_index(
self, model: peewee.Model, *columns: str, unique=False
) -> peewee.Model:
"""Create indexes."""
model._meta.indexes.append((columns, unique))
columns_ = []
for col in columns:
field = model._meta.fields.get(col)
if len(columns) == 1:
field.unique = unique
field.index = not unique
if isinstance(field, peewee.ForeignKeyField):
col = col + "_id"
columns_.append(col)
self.operations.append(
self.migrator.add_index(model._meta.table_name, columns_, unique=unique)
)
return model
@get_model
def drop_index(self, model: peewee.Model, *columns: str) -> peewee.Model:
"""Drop indexes."""
columns_ = []
for col in columns:
field = model._meta.fields.get(col)
if not field:
continue
if len(columns) == 1:
field.unique = field.index = False
if isinstance(field, peewee.ForeignKeyField):
col = col + "_id"
columns_.append(col)
index_name = make_index_name(model._meta.table_name, columns_)
model._meta.indexes = [
(cols, _) for (cols, _) in model._meta.indexes if columns != cols
]
self.operations.append(
self.migrator.drop_index(model._meta.table_name, index_name)
)
return model
@get_model
def add_not_null(self, model: peewee.Model, *names: str) -> peewee.Model:
"""Add not null."""
for name in names:
field = model._meta.fields[name]
field.null = False
self.operations.append(
self.migrator.add_not_null(model._meta.table_name, field.column_name)
)
return model
@get_model
def drop_not_null(self, model: peewee.Model, *names: str) -> peewee.Model:
"""Drop not null."""
for name in names:
field = model._meta.fields[name]
field.null = True
self.operations.append(
self.migrator.drop_not_null(model._meta.table_name, field.column_name)
)
return model
@get_model
def add_default(
self, model: peewee.Model, name: str, default: t.Any
) -> peewee.Model:
"""Add default."""
field = model._meta.fields[name]
model._meta.defaults[field] = field.default = default
self.operations.append(
self.migrator.apply_default(model._meta.table_name, name, field)
)
return model
# noinspection PyProtectedMember
class MigrationManager(object):
filemask = re.compile(r"[\d]+_[^\.]+\.py$")
def __init__(self, database: t.Union[peewee.Database, peewee.Proxy], helper):
"""
Initializes the migration manager.
"""
if not isinstance(database, (peewee.Database, peewee.Proxy)):
raise RuntimeError("Invalid database: {}".format(database))
self.database = database
self.helper = helper
@cached_property
def model(self) -> t.Type[MigrateHistory]:
"""
Initialize and cache the MigrationHistory model.
"""
MigrateHistory._meta.database = self.database
MigrateHistory._meta.table_name = "migratehistory"
MigrateHistory._meta.schema = None
MigrateHistory.create_table(True)
return MigrateHistory
@property
def done(self) -> t.List[str]:
"""
Scans migrations in the database.
"""
return [mm.name for mm in self.model.select().order_by(self.model.id)]
@property
def todo(self):
"""
Scans migrations in the file system.
"""
if not os.path.exists(self.helper.migration_dir):
logger.warning(
"Migration directory: {} does not exist.".format(
self.helper.migration_dir
)
)
os.makedirs(self.helper.migration_dir)
return sorted(
f[:-3]
for f in os.listdir(self.helper.migration_dir)
if self.filemask.match(f)
)
@property
def diff(self) -> t.List[str]:
"""
Calculates difference between the filesystem and the database.
"""
done = set(self.done)
return [name for name in self.todo if name not in done]
@cached_property
def migrator(self) -> Migrator:
"""
Create migrator and setup it with fake migrations.
"""
migrator = Migrator(self.database)
for name in self.done:
self.up_one(name, migrator, True)
return migrator
def compile(self, name, migrate="", rollback=""):
"""
Compiles a migration.
"""
name = datetime.utcnow().strftime("%Y%m%d%H%M%S") + "_" + name
filename = name + ".py"
path = os.path.join(self.helper.migration_dir, filename)
with open(path, "w") as f:
f.write(
MIGRATE_TEMPLATE.format(
migrate=migrate, rollback=rollback, name=filename
)
)
return name
def create(self, name: str = "auto", auto: bool = False) -> t.Optional[str]:
"""
Creates a migration.
"""
migrate = rollback = ""
if auto:
raise NotImplementedError
logger.info('Creating migration "{}"'.format(name))
name = self.compile(name, migrate, rollback)
logger.info('Migration has been created as "{}"'.format(name))
return name
def clear(self):
"""Clear migrations."""
self.model.delete().execute()
def up(self, name: t.Optional[str] = None):
"""
Runs all unapplied migrations.
"""
logger.info("Starting migrations")
Console.info("Starting migrations")
done = []
diff = self.diff
if not diff:
logger.info("There is nothing to migrate")
Console.info("There is nothing to migrate")
return done
migrator = self.migrator
for mname in diff:
done.append(self.up_one(mname, self.migrator))
if name and name == mname:
break
return done
def read(self, name: str):
"""
Reads a migration from a file.
"""
call_params = dict()
if Helpers.is_os_windows() and sys.version_info >= (3, 0):
# if system is windows - force utf-8 encoding
call_params["encoding"] = "utf-8"
with open(
os.path.join(self.helper.migration_dir, name + ".py"), **call_params
) as f:
code = f.read()
scope = {}
code = compile(code, "<string>", "exec", dont_inherit=True)
exec(code, scope, None)
return scope.get("migrate", lambda m, d: None), scope.get(
"rollback", lambda m, d: None
)
def up_one(
self, name: str, migrator: Migrator, fake: bool = False, rollback: bool = False
) -> str:
"""
Runs a migration with a given name.
"""
try:
migrate_fn, rollback_fn = self.read(name)
if fake:
migrate_fn(migrator, self.database)
migrator.clean()
return name
with self.database.transaction():
if rollback:
logger.info('Rolling back "{}"'.format(name))
rollback_fn(migrator, self.database)
migrator.run()
self.model.delete().where(self.model.name == name).execute()
else:
logger.info('Migrate "{}"'.format(name))
migrate_fn(migrator, self.database)
migrator.run()
if name not in self.done:
self.model.create(name=name)
logger.info('Done "{}"'.format(name))
return name
except Exception:
self.database.rollback()
operation_name = "Rollback" if rollback else "Migration"
logger.exception("{} failed: {}".format(operation_name, name))
raise
def down(self):
"""
Rolls back migrations.
"""
if not self.done:
raise RuntimeError("No migrations are found.")
name = self.done[-1]
migrator = self.migrator
self.up_one(name, migrator, False, True)
logger.warning("Rolled back migration: {}".format(name))

View File

@ -3,25 +3,25 @@ from enum import Enum
class PermissionHelper:
@staticmethod
def both_have_perm(a: str, b: str, permission_tested: Enum):
return permission_helper.combine_perm_bool(
a[permission_tested.value], b[permission_tested.value]
def both_have_perm(
permission_mask_a: str, permission_mask_b: str, permission_tested: Enum
):
return PermissionHelper.combine_perm_bool(
permission_mask_a[permission_tested.value],
permission_mask_b[permission_tested.value],
)
@staticmethod
def combine_perm(a: str, b: str) -> str:
return "1" if (a == "1" and b == "1") else "0"
def combine_perm(permission_mask_a: str, permission_mask_b: str) -> str:
return "1" if (permission_mask_a == "1" and permission_mask_b == "1") else "0"
@staticmethod
def combine_perm_bool(a: str, b: str) -> bool:
return a == "1" and b == "1"
def combine_perm_bool(permission_mask_a: str, permission_mask_b: str) -> bool:
return permission_mask_a == "1" and permission_mask_b == "1"
@staticmethod
def combine_masks(permission_mask_a: str, permission_mask_b: str) -> str:
both_masks = zip(list(permission_mask_a), list(permission_mask_b))
return "".join(
map(lambda x: permission_helper.combine_perm(x[0], x[1]), both_masks)
map(lambda x: PermissionHelper.combine_perm(x[0], x[1]), both_masks)
)
permission_helper = PermissionHelper()

File diff suppressed because it is too large Load Diff

View File

@ -4,24 +4,15 @@ import logging
import threading
import asyncio
import datetime
from tzlocal import get_localzone
from apscheduler.events import EVENT_JOB_EXECUTED
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from app.classes.controllers.users_controller import Users_Controller
from app.classes.minecraft.serverjars import server_jar_obj
from app.classes.models.management import management_helper
from app.classes.models.users import users_helper
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
from app.classes.models.management import HelpersManagement
from app.classes.models.users import HelperUsers
from app.classes.shared.console import Console
from app.classes.web.tornado_handler import Webserver
from app.classes.web.websocket_helper import websocket_helper
try:
from tzlocal import get_localzone
from apscheduler.events import EVENT_JOB_EXECUTED
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
except ModuleNotFoundError as err:
helper.auto_installer_fix(err)
logger = logging.getLogger("apscheduler")
scheduler_intervals = {
@ -41,14 +32,15 @@ scheduler_intervals = {
class TasksManager:
def __init__(self, controller):
def __init__(self, helper, controller):
self.helper = helper
self.controller = controller
self.tornado = Webserver(controller, self)
self.tornado = Webserver(helper, controller, self)
self.tz = get_localzone()
self.scheduler = BackgroundScheduler(timezone=str(self.tz))
self.users_controller = Users_Controller()
self.users_controller = self.controller.users
self.webserver_thread = threading.Thread(
target=self.tornado.run_tornado, daemon=True, name="tornado_thread"
@ -78,7 +70,7 @@ class TasksManager:
return self.main_thread_exiting
def reload_schedule_from_db(self):
jobs = management_helper.get_schedules_enabled()
jobs = HelpersManagement.get_schedules_enabled()
logger.info("Reload from DB called. Current enabled schedules: ")
for item in jobs:
logger.info(f"JOB: {item}")
@ -86,19 +78,19 @@ class TasksManager:
def command_watcher(self):
while True:
# select any commands waiting to be processed
commands = management_helper.get_unactioned_commands()
for c in commands:
commands = HelpersManagement.get_unactioned_commands()
for cmd in commands:
try:
svr = self.controller.get_server_obj(c.server_id)
svr = self.controller.get_server_obj(cmd.server_id)
except:
logger.error(
"Server value requested does note exist! "
"Purging item from waiting commands."
)
management_helper.mark_command_complete(c.command_id)
HelpersManagement.mark_command_complete(cmd.command_id)
user_id = c.user_id
command = c.command
user_id = cmd.user_id
command = cmd.command
if command == "start_server":
svr.run_threaded_server(user_id)
@ -116,19 +108,19 @@ class TasksManager:
svr.jar_update()
else:
svr.send_command(command)
management_helper.mark_command_complete(c.command_id)
HelpersManagement.mark_command_complete(cmd.command_id)
time.sleep(1)
def _main_graceful_exit(self):
try:
os.remove(helper.session_file)
os.remove(self.helper.session_file)
self.controller.stop_all_servers()
except:
logger.info("Caught error during shutdown", exc_info=True)
logger.info("***** Crafty Shutting Down *****\n\n")
console.info("***** Crafty Shutting Down *****\n\n")
Console.info("***** Crafty Shutting Down *****\n\n")
self.main_thread_exiting = True
def start_webserver(self):
@ -136,7 +128,7 @@ class TasksManager:
def reload_webserver(self):
self.tornado.stop_web_server()
console.info("Waiting 3 seconds")
Console.info("Waiting 3 seconds")
time.sleep(3)
self.webserver_thread = threading.Thread(
target=self.tornado.run_tornado, daemon=True, name="tornado_thread"
@ -148,20 +140,20 @@ class TasksManager:
def start_scheduler(self):
logger.info("Launching Scheduler Thread...")
console.info("Launching Scheduler Thread...")
Console.info("Launching Scheduler Thread...")
self.schedule_thread.start()
logger.info("Launching command thread...")
console.info("Launching command thread...")
Console.info("Launching command thread...")
self.command_thread.start()
logger.info("Launching log watcher...")
console.info("Launching log watcher...")
Console.info("Launching log watcher...")
self.log_watcher_thread.start()
logger.info("Launching realtime thread...")
console.info("Launching realtime thread...")
Console.info("Launching realtime thread...")
self.realtime_thread.start()
def scheduler_thread(self):
schedules = management_helper.get_schedules_enabled()
schedules = HelpersManagement.get_schedules_enabled()
self.scheduler.add_listener(self.schedule_watcher, mask=EVENT_JOB_EXECUTED)
# self.scheduler.add_job(
# self.scheduler.print_jobs, "interval", seconds=10, id="-1"
@ -173,7 +165,7 @@ class TasksManager:
if schedule.cron_string != "":
try:
self.scheduler.add_job(
management_helper.add_command,
HelpersManagement.add_command,
CronTrigger.from_crontab(
schedule.cron_string, timezone=str(self.tz)
),
@ -186,16 +178,18 @@ class TasksManager:
],
)
except Exception as e:
console.error(f"Failed to schedule task with error: {e}.")
console.warning("Removing failed task from DB.")
Console.error(f"Failed to schedule task with error: {e}.")
Console.warning("Removing failed task from DB.")
logger.error(f"Failed to schedule task with error: {e}.")
logger.warning("Removing failed task from DB.")
# remove items from DB if task fails to add to apscheduler
management_helper.delete_scheduled_task(schedule.schedule_id)
self.controller.management_helper.delete_scheduled_task(
schedule.schedule_id
)
else:
if schedule.interval_type == "hours":
self.scheduler.add_job(
management_helper.add_command,
HelpersManagement.add_command,
"cron",
minute=0,
hour="*/" + str(schedule.interval),
@ -209,7 +203,7 @@ class TasksManager:
)
elif schedule.interval_type == "minutes":
self.scheduler.add_job(
management_helper.add_command,
HelpersManagement.add_command,
"cron",
minute="*/" + str(schedule.interval),
id=str(schedule.schedule_id),
@ -223,7 +217,7 @@ class TasksManager:
elif schedule.interval_type == "days":
curr_time = schedule.start_time.split(":")
self.scheduler.add_job(
management_helper.add_command,
HelpersManagement.add_command,
"cron",
day="*/" + str(schedule.interval),
hour=curr_time[0],
@ -243,7 +237,7 @@ class TasksManager:
logger.info(f"JOB: {item}")
def schedule_job(self, job_data):
sch_id = management_helper.create_scheduled_task(
sch_id = HelpersManagement.create_scheduled_task(
job_data["server_id"],
job_data["action"],
job_data["interval"],
@ -260,13 +254,13 @@ class TasksManager:
# Checks to make sure some doofus didn't actually make the newly
# created task a child of itself.
if str(job_data["parent"]) == str(sch_id):
management_helper.update_scheduled_task(sch_id, {"parent": None})
HelpersManagement.update_scheduled_task(sch_id, {"parent": None})
# Check to see if it's enabled and is not a chain reaction.
if job_data["enabled"] and job_data["interval_type"] != "reaction":
if job_data["cron_string"] != "":
try:
self.scheduler.add_job(
management_helper.add_command,
HelpersManagement.add_command,
CronTrigger.from_crontab(
job_data["cron_string"], timezone=str(self.tz)
),
@ -279,16 +273,16 @@ class TasksManager:
],
)
except Exception as e:
console.error(f"Failed to schedule task with error: {e}.")
console.warning("Removing failed task from DB.")
Console.error(f"Failed to schedule task with error: {e}.")
Console.warning("Removing failed task from DB.")
logger.error(f"Failed to schedule task with error: {e}.")
logger.warning("Removing failed task from DB.")
# remove items from DB if task fails to add to apscheduler
management_helper.delete_scheduled_task(sch_id)
self.controller.management_helper.delete_scheduled_task(sch_id)
else:
if job_data["interval_type"] == "hours":
self.scheduler.add_job(
management_helper.add_command,
HelpersManagement.add_command,
"cron",
minute=0,
hour="*/" + str(job_data["interval"]),
@ -302,7 +296,7 @@ class TasksManager:
)
elif job_data["interval_type"] == "minutes":
self.scheduler.add_job(
management_helper.add_command,
HelpersManagement.add_command,
"cron",
minute="*/" + str(job_data["interval"]),
id=str(sch_id),
@ -316,7 +310,7 @@ class TasksManager:
elif job_data["interval_type"] == "days":
curr_time = job_data["start_time"].split(":")
self.scheduler.add_job(
management_helper.add_command,
HelpersManagement.add_command,
"cron",
day="*/" + str(job_data["interval"]),
hour=curr_time[0],
@ -335,18 +329,18 @@ class TasksManager:
logger.info(f"JOB: {item}")
def remove_all_server_tasks(self, server_id):
schedules = management_helper.get_schedules_by_server(server_id)
schedules = HelpersManagement.get_schedules_by_server(server_id)
for schedule in schedules:
if schedule.interval != "reaction":
self.remove_job(schedule.schedule_id)
def remove_job(self, sch_id):
job = management_helper.get_scheduled_task_model(sch_id)
for schedule in management_helper.get_child_schedules(sch_id):
management_helper.update_scheduled_task(
job = HelpersManagement.get_scheduled_task_model(sch_id)
for schedule in HelpersManagement.get_child_schedules(sch_id):
self.controller.management_helper.update_scheduled_task(
schedule.schedule_id, {"parent": None}
)
management_helper.delete_scheduled_task(sch_id)
self.controller.management_helper.delete_scheduled_task(sch_id)
if job.enabled and job.interval_type != "reaction":
self.scheduler.remove_job(str(sch_id))
logger.info(f"Job with ID {sch_id} was deleted.")
@ -358,11 +352,11 @@ class TasksManager:
)
def update_job(self, sch_id, job_data):
management_helper.update_scheduled_task(sch_id, job_data)
HelpersManagement.update_scheduled_task(sch_id, job_data)
# Checks to make sure some doofus didn't actually make the newly
# created task a child of itself.
if str(job_data["parent"]) == str(sch_id):
management_helper.update_scheduled_task(sch_id, {"parent": None})
HelpersManagement.update_scheduled_task(sch_id, {"parent": None})
try:
if job_data["interval"] != "reaction":
self.scheduler.remove_job(str(sch_id))
@ -377,7 +371,7 @@ class TasksManager:
if job_data["cron_string"] != "":
try:
self.scheduler.add_job(
management_helper.add_command,
HelpersManagement.add_command,
CronTrigger.from_crontab(
job_data["cron_string"], timezone=str(self.tz)
),
@ -390,13 +384,13 @@ class TasksManager:
],
)
except Exception as e:
console.error(f"Failed to schedule task with error: {e}.")
console.info("Removing failed task from DB.")
management_helper.delete_scheduled_task(sch_id)
Console.error(f"Failed to schedule task with error: {e}.")
Console.info("Removing failed task from DB.")
self.controller.management_helper.delete_scheduled_task(sch_id)
else:
if job_data["interval_type"] == "hours":
self.scheduler.add_job(
management_helper.add_command,
HelpersManagement.add_command,
"cron",
minute=0,
hour="*/" + str(job_data["interval"]),
@ -410,7 +404,7 @@ class TasksManager:
)
elif job_data["interval_type"] == "minutes":
self.scheduler.add_job(
management_helper.add_command,
HelpersManagement.add_command,
"cron",
minute="*/" + str(job_data["interval"]),
id=str(sch_id),
@ -424,7 +418,7 @@ class TasksManager:
elif job_data["interval_type"] == "days":
curr_time = job_data["start_time"].split(":")
self.scheduler.add_job(
management_helper.add_command,
HelpersManagement.add_command,
"cron",
day="*/" + str(job_data["interval"]),
hour=curr_time[0],
@ -450,10 +444,12 @@ class TasksManager:
def schedule_watcher(self, event):
if not event.exception:
if str(event.job_id).isnumeric():
task = management_helper.get_scheduled_task_model(int(event.job_id))
management_helper.add_to_audit_log_raw(
task = self.controller.management.get_scheduled_task_model(
int(event.job_id)
)
self.controller.management.add_to_audit_log_raw(
"system",
users_helper.get_user_id_by_name("system"),
HelperUsers.get_user_id_by_name("system"),
task.server_id,
f"Task with id {task.schedule_id} completed successfully",
"127.0.0.1",
@ -465,7 +461,7 @@ class TasksManager:
# check for any child tasks for this. It's kind of backward,
# but this makes DB management a lot easier. One to one
# instead of one to many.
for schedule in management_helper.get_child_schedules_by_server(
for schedule in HelpersManagement.get_child_schedules_by_server(
task.schedule_id, task.server_id
):
# event job ID's are strings so we need to look at
@ -476,7 +472,7 @@ class TasksManager:
seconds=schedule.delay
)
self.scheduler.add_job(
management_helper.add_command,
HelpersManagement.add_command,
"date",
run_date=delaytime,
id=str(schedule.schedule_id),
@ -496,11 +492,11 @@ class TasksManager:
logger.error(f"Task failed with error: {event.exception}")
def start_stats_recording(self):
stats_update_frequency = helper.get_setting("stats_update_frequency")
stats_update_frequency = self.helper.get_setting("stats_update_frequency")
logger.info(
f"Stats collection frequency set to {stats_update_frequency} seconds"
)
console.info(
Console.info(
f"Stats collection frequency set to {stats_update_frequency} seconds"
)
@ -516,36 +512,39 @@ class TasksManager:
def serverjar_cache_refresher(self):
logger.info("Refreshing serverjars.com cache on start")
server_jar_obj.refresh_cache()
self.controller.server_jars.refresh_cache()
logger.info("Scheduling Serverjars.com cache refresh service every 12 hours")
self.scheduler.add_job(
server_jar_obj.refresh_cache, "interval", hours=12, id="serverjars"
self.controller.server_jars.refresh_cache,
"interval",
hours=12,
id="serverjars",
)
def realtime(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
host_stats = management_helper.get_latest_hosts_stats()
host_stats = HelpersManagement.get_latest_hosts_stats()
while True:
if host_stats.get(
"cpu_usage"
) != management_helper.get_latest_hosts_stats().get(
) != HelpersManagement.get_latest_hosts_stats().get(
"cpu_usage"
) or host_stats.get(
"mem_percent"
) != management_helper.get_latest_hosts_stats().get(
) != HelpersManagement.get_latest_hosts_stats().get(
"mem_percent"
):
# Stats are different
host_stats = management_helper.get_latest_hosts_stats()
if len(websocket_helper.clients) > 0:
host_stats = HelpersManagement.get_latest_hosts_stats()
if len(self.helper.websocket_helper.clients) > 0:
# There are clients
websocket_helper.broadcast_page(
self.helper.websocket_helper.broadcast_page(
"/panel/dashboard",
"update_host_stats",
{
@ -557,6 +556,7 @@ class TasksManager:
"mem_usage": host_stats.get("mem_usage"),
},
)
time.sleep(1)
def log_watcher(self):
self.controller.servers.check_for_old_logs()

View File

@ -3,15 +3,17 @@ import logging
import os
import typing as t
from app.classes.shared.console import console
from app.classes.shared.helpers import helper
from app.classes.shared.console import Console
logger = logging.getLogger(__name__)
class Translation:
def __init__(self):
self.translations_path = os.path.join(helper.root_dir, "app", "translations")
def __init__(self, helper):
self.helper = helper
self.translations_path = os.path.join(
self.helper.root_dir, "app", "translations"
)
self.cached_translation = None
self.cached_translation_lang = None
@ -59,7 +61,7 @@ class Translation:
f"Translation File Error: page {page} "
f"does not exist for lang {language}"
)
console.error(
Console.error(
f"Translation File Error: page {page} "
f"does not exist for lang {language}"
)
@ -73,7 +75,7 @@ class Translation:
f"Translation File Error: word {word} does not exist on page "
f"{page} for lang {language}"
)
console.error(
Console.error(
f"Translation File Error: word {word} does not exist on page "
f"{page} for lang {language}"
)
@ -83,10 +85,7 @@ class Translation:
logger.critical(
f"Translation File Error: Unable to read {language_file} due to {e}"
)
console.critical(
Console.critical(
f"Translation File Error: Unable to read {language_file} due to {e}"
)
return None
translation = Translation()

View File

@ -3,23 +3,16 @@ import html
import re
import logging
import time
import bleach
import tornado.web
import tornado.escape
from app.classes.models.server_permissions import Enum_Permissions_Server
from app.classes.shared.console import console
from app.classes.shared.helpers import helper
from app.classes.shared.translation import translation
from app.classes.models.server_permissions import EnumPermissionsServer
from app.classes.shared.console import Console
from app.classes.shared.helpers import Helpers
from app.classes.shared.server import ServerOutBuf
from app.classes.web.websocket_helper import websocket_helper
from app.classes.web.base_handler import BaseHandler
try:
import bleach
import tornado.web
import tornado.escape
except ModuleNotFoundError as ex:
helper.auto_installer_fix(ex)
logger = logging.getLogger(__name__)
@ -67,19 +60,19 @@ class AjaxHandler(BaseHandler):
)
if full_log:
log_lines = helper.get_setting("max_log_lines")
data = helper.tail_file(
helper.get_os_understandable_path(server_data["log_path"]),
log_lines = self.helper.get_setting("max_log_lines")
data = Helpers.tail_file(
Helpers.get_os_understandable_path(server_data["log_path"]),
log_lines,
)
else:
data = ServerOutBuf.lines.get(server_id, [])
for d in data:
for line in data:
try:
d = re.sub("(\033\\[(0;)?[0-9]*[A-z]?(;[0-9])?m?)|(> )", "", d)
d = re.sub("[A-z]{2}\b\b", "", d)
line = helper.log_colors(html.escape(d))
line = re.sub("(\033\\[(0;)?[0-9]*[A-z]?(;[0-9])?m?)", "", line)
line = re.sub("[A-z]{2}\b\b", "", line)
line = self.helper.log_colors(html.escape(line))
self.write(f"{line}<br />")
# self.write(d.encode("utf-8"))
@ -87,7 +80,7 @@ class AjaxHandler(BaseHandler):
logger.warning(f"Skipping Log Line due to error: {e}")
elif page == "announcements":
data = helper.get_announcements()
data = Helpers.get_announcements()
page_data["notify_data"] = data
self.render_page("ajax/notify.html", page_data)
@ -95,9 +88,9 @@ class AjaxHandler(BaseHandler):
path = self.get_argument("path", None)
self.write(
helper.get_os_understandable_path(path)
Helpers.get_os_understandable_path(path)
+ "\n"
+ helper.generate_zip_tree(path)
+ Helpers.generate_zip_tree(path)
)
self.finish()
@ -105,9 +98,9 @@ class AjaxHandler(BaseHandler):
path = self.get_argument("path", None)
self.write(
helper.get_os_understandable_path(path)
Helpers.get_os_understandable_path(path)
+ "\n"
+ helper.generate_zip_dir(path)
+ Helpers.generate_zip_dir(path)
)
self.finish()
@ -141,15 +134,15 @@ class AjaxHandler(BaseHandler):
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
<input type="checkbox" class="checkBoxClass" name="root_path" value="{dpath}" checked>
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
<i class="far fa-folder"></i>
<i class="far fa-folder-open"></i>
<i style="color: #8862e0;" class="far fa-folder"></i>
<i style="color: #8862e0;" class="far fa-folder-open"></i>
<strong>{filename}</strong>
</span>
</input></div><li>
\n"""
else:
output += f"""<li
class="tree-nested d-block tree-ctx-item tree-file"
class="d-block tree-ctx-item tree-file"
data-path="{dpath}"
data-name="{filename}"
onclick=""><input type='checkbox' class="checkBoxClass" name='root_path' value="{dpath}" checked><span style="margin-right: 6px;">
@ -161,21 +154,21 @@ class AjaxHandler(BaseHandler):
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
<input type="checkbox" class="checkBoxClass" name="root_path" value="{dpath}">
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
<i class="far fa-folder"></i>
<i class="far fa-folder-open"></i>
<i style="color: #8862e0;" class="far fa-folder"></i>
<i style="color: #8862e0;" class="far fa-folder-open"></i>
<strong>{filename}</strong>
</span>
</input></div><li>
\n"""
else:
output += f"""<li
class="tree-nested d-block tree-ctx-item tree-file"
class="d-block tree-ctx-item tree-file"
data-path="{dpath}"
data-name="{filename}"
onclick=""><input type='checkbox' class="checkBoxClass" name='root_path' value="{dpath}">
<span style="margin-right: 6px;"><i class="far fa-file">
</i></span></input>{filename}</li>"""
self.write(helper.get_os_understandable_path(folder) + "\n" + output)
self.write(Helpers.get_os_understandable_path(folder) + "\n" + output)
self.finish()
elif page == "get_backup_dir":
@ -205,7 +198,7 @@ class AjaxHandler(BaseHandler):
if os.path.isdir(rel):
output += f"""<li class="tree-item" data-path="{dpath}">
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}" class="tree-caret tree-ctx-item tree-folder">
<input type="checkbox" name="root_path" value="{dpath}">
<input type="checkbox" name="root_path" value="{dpath}" checked>
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}" data-name="{filename}" onclick="getDirView(event)">
<i class="far fa-folder"></i>
<i class="far fa-folder-open"></i>
@ -217,7 +210,7 @@ class AjaxHandler(BaseHandler):
class="tree-item tree-nested d-block tree-ctx-item tree-file"
data-path="{dpath}"
data-name="{filename}"
onclick=""><input type='checkbox' name='root_path' value='{dpath}'><span style="margin-right: 6px;">
onclick=""><input type='checkbox' name='root_path' value='{dpath}' checked><span style="margin-right: 6px;">
<i class="far fa-file"></i></span></input>{filename}</li>"""
else:
@ -240,7 +233,7 @@ class AjaxHandler(BaseHandler):
<span style="margin-right: 6px;"><i class="far fa-file">
</i></span></input>{filename}</li>"""
self.write(helper.get_os_understandable_path(folder) + "\n" + output)
self.write(Helpers.get_os_understandable_path(folder) + "\n" + output)
self.finish()
elif page == "get_dir":
@ -252,13 +245,13 @@ class AjaxHandler(BaseHandler):
else:
server_id = bleach.clean(server_id)
if helper.validate_traversal(
if Helpers.validate_traversal(
self.controller.servers.get_server_data_by_id(server_id)["path"], path
):
self.write(
helper.get_os_understandable_path(path)
Helpers.get_os_understandable_path(path)
+ "\n"
+ helper.generate_dir(path)
+ Helpers.generate_dir(path)
)
self.finish()
@ -272,14 +265,14 @@ class AjaxHandler(BaseHandler):
server_id = self.get_argument("id", None)
permissions = {
"Commands": Enum_Permissions_Server.Commands,
"Terminal": Enum_Permissions_Server.Terminal,
"Logs": Enum_Permissions_Server.Logs,
"Schedule": Enum_Permissions_Server.Schedule,
"Backup": Enum_Permissions_Server.Backup,
"Files": Enum_Permissions_Server.Files,
"Config": Enum_Permissions_Server.Config,
"Players": Enum_Permissions_Server.Players,
"Commands": EnumPermissionsServer.COMMANDS,
"Terminal": EnumPermissionsServer.TERMINAL,
"Logs": EnumPermissionsServer.LOGS,
"Schedule": EnumPermissionsServer.SCHEDULE,
"Backup": EnumPermissionsServer.BACKUP,
"Files": EnumPermissionsServer.FILES,
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
}
user_perms = self.controller.server_perms.get_user_id_permissions_list(
exec_user["user_id"], server_id
@ -291,7 +284,7 @@ class AjaxHandler(BaseHandler):
if server_id is None:
logger.warning("Server ID not found in send_command ajax call")
console.warning("Server ID not found in send_command ajax call")
Console.warning("Server ID not found in send_command ajax call")
srv_obj = self.controller.get_server_obj(server_id)
@ -390,11 +383,11 @@ class AjaxHandler(BaseHandler):
server_data = self.controller.servers.get_server_data_by_id(server_id)
if server_data["type"] == "minecraft-java":
backup_path = svr_obj.backup_path
if helper.validate_traversal(backup_path, zip_name):
tempDir = helper.unzip_backup_archive(backup_path, zip_name)
if Helpers.validate_traversal(backup_path, zip_name):
temp_dir = Helpers.unzip_backup_archive(backup_path, zip_name)
new_server = self.controller.import_zip_server(
svr_obj.server_name,
tempDir,
temp_dir,
server_data["executable"],
"1",
"2",
@ -410,11 +403,11 @@ class AjaxHandler(BaseHandler):
else:
backup_path = svr_obj.backup_path
if helper.validate_traversal(backup_path, zip_name):
tempDir = helper.unzip_backup_archive(backup_path, zip_name)
if Helpers.validate_traversal(backup_path, zip_name):
temp_dir = Helpers.unzip_backup_archive(backup_path, zip_name)
new_server = self.controller.import_bedrock_zip_server(
svr_obj.server_name,
tempDir,
temp_dir,
server_data["executable"],
server_data["server_port"],
)
@ -428,23 +421,27 @@ class AjaxHandler(BaseHandler):
elif page == "unzip_server":
path = self.get_argument("path", None)
if helper.check_file_exists(path):
helper.unzipServer(path, exec_user["user_id"])
if Helpers.check_file_exists(path):
self.helper.unzip_server(path, exec_user["user_id"])
else:
user_id = exec_user["user_id"]
if user_id:
time.sleep(5)
user_lang = self.controller.users.get_user_lang_by_id(user_id)
websocket_helper.broadcast_user(
self.helper.websocket_helper.broadcast_user(
user_id,
"send_start_error",
{"error": translation.translate("error", "no-file", user_lang)},
{
"error": self.helper.translation.translate(
"error", "no-file", user_lang
)
},
)
return
elif page == "backup_select":
path = self.get_argument("path", None)
helper.backup_select(path, exec_user["user_id"])
self.helper.backup_select(path, exec_user["user_id"])
return
@tornado.web.authenticated
@ -457,14 +454,14 @@ class AjaxHandler(BaseHandler):
server_id = self.get_argument("id", None)
permissions = {
"Commands": Enum_Permissions_Server.Commands,
"Terminal": Enum_Permissions_Server.Terminal,
"Logs": Enum_Permissions_Server.Logs,
"Schedule": Enum_Permissions_Server.Schedule,
"Backup": Enum_Permissions_Server.Backup,
"Files": Enum_Permissions_Server.Files,
"Config": Enum_Permissions_Server.Config,
"Players": Enum_Permissions_Server.Players,
"Commands": EnumPermissionsServer.COMMANDS,
"Terminal": EnumPermissionsServer.TERMINAL,
"Logs": EnumPermissionsServer.LOGS,
"Schedule": EnumPermissionsServer.SCHEDULE,
"Backup": EnumPermissionsServer.BACKUP,
"Files": EnumPermissionsServer.FILES,
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
}
user_perms = self.controller.server_perms.get_user_id_permissions_list(
exec_user["user_id"], server_id
@ -481,12 +478,12 @@ class AjaxHandler(BaseHandler):
if not superuser:
self.redirect("/panel/error?error=Unauthorized access to Backups")
return
file_path = helper.get_os_understandable_path(
file_path = Helpers.get_os_understandable_path(
self.get_body_argument("file_path", default=None, strip=True)
)
server_id = self.get_argument("id", None)
console.warning(f"Delete {file_path} for server {server_id}")
Console.warning(f"Delete {file_path} for server {server_id}")
if not self.check_server_id(server_id, "del_backup"):
return
@ -495,21 +492,22 @@ class AjaxHandler(BaseHandler):
server_info = self.controller.servers.get_server_data_by_id(server_id)
if not (
helper.in_path(
helper.get_os_understandable_path(server_info["path"]), file_path
Helpers.in_path(
Helpers.get_os_understandable_path(server_info["path"]), file_path
)
or helper.in_path(
helper.get_os_understandable_path(server_info["backup_path"]),
or Helpers.in_path(
Helpers.get_os_understandable_path(server_info["backup_path"]),
file_path,
)
) or not helper.check_file_exists(os.path.abspath(file_path)):
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
logger.warning(f"Invalid path in del_backup ajax call ({file_path})")
console.warning(f"Invalid path in del_backup ajax call ({file_path})")
Console.warning(f"Invalid path in del_backup ajax call ({file_path})")
return
# Delete the file
if helper.validate_traversal(
helper.get_os_understandable_path(server_info["backup_path"]), file_path
if Helpers.validate_traversal(
Helpers.get_os_understandable_path(server_info["backup_path"]),
file_path,
):
os.remove(file_path)
@ -566,7 +564,7 @@ class AjaxHandler(BaseHandler):
logger.warning(
f"Server ID not defined in {page_name} ajax call ({server_id})"
)
console.warning(
Console.warning(
f"Server ID not defined in {page_name} ajax call ({server_id})"
)
return
@ -578,7 +576,7 @@ class AjaxHandler(BaseHandler):
logger.warning(
f"Server ID not found in {page_name} ajax call ({server_id})"
)
console.warning(
Console.warning(
f"Server ID not found in {page_name} ajax call ({server_id})"
)
return

View File

@ -1,6 +1,9 @@
from datetime import datetime
import logging
import re
from app.classes.controllers.crafty_perms_controller import EnumPermissionsCrafty
from app.classes.controllers.server_perms_controller import EnumPermissionsServer
from app.classes.web.base_handler import BaseHandler
logger = logging.getLogger(__name__)
@ -13,6 +16,10 @@ class ApiHandler(BaseHandler):
self.set_status(status)
self.write(data)
def check_xsrf_cookie(self):
# Disable CSRF protection on API routes
pass
def access_denied(self, user, reason=""):
if reason:
reason = " because " + reason
@ -34,10 +41,24 @@ class ApiHandler(BaseHandler):
)
def authenticate_user(self) -> bool:
self.permissions = {
"Commands": EnumPermissionsServer.COMMANDS,
"Terminal": EnumPermissionsServer.TERMINAL,
"Logs": EnumPermissionsServer.LOGS,
"Schedule": EnumPermissionsServer.SCHEDULE,
"Backup": EnumPermissionsServer.BACKUP,
"Files": EnumPermissionsServer.FILES,
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
"Server_Creation": EnumPermissionsCrafty.SERVER_CREATION,
"User_Config": EnumPermissionsCrafty.USER_CONFIG,
"Roles_Config": EnumPermissionsCrafty.ROLES_CONFIG,
}
try:
logger.debug("Searching for specified token")
api_token = self.get_argument("token", "")
self.api_token = api_token
if api_token is None and self.request.headers.get("Authorization"):
api_token = bearer_pattern.sub(
"", self.request.headers.get("Authorization")
@ -50,7 +71,6 @@ class ApiHandler(BaseHandler):
if user_data:
# Login successful! Check perms
logger.info(f"User {user_data['username']} has authenticated to API")
# TODO: Role check
return True # This is to set the "authenticated"
else:
@ -75,12 +95,28 @@ class ServersStats(ApiHandler):
def get(self):
"""Get details about all servers"""
authenticated = self.authenticate_user()
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
if not authenticated:
return
if user_obj["superuser"]:
raw_stats = self.controller.servers.get_all_servers_stats()
else:
raw_stats = self.controller.servers.get_authorized_servers_stats(
user_obj["user_id"]
)
stats = []
for rs in raw_stats:
s = {}
for k, v in rs["server_data"].items():
if isinstance(v, datetime):
s[k] = v.timestamp()
else:
s[k] = v
stats.append(s)
# Get server stats
# TODO Check perms
self.finish(self.write({"servers": self.controller.stats.get_servers_stats()}))
self.finish(self.write({"servers": stats}))
class NodeStats(ApiHandler):
@ -92,5 +128,311 @@ class NodeStats(ApiHandler):
# Get node stats
node_stats = self.controller.stats.get_node_stats()
node_stats.pop("servers")
self.finish(self.write(node_stats))
self.return_response(200, {"code": node_stats["node_stats"]})
class SendCommand(ApiHandler):
def post(self):
user = self.authenticate_user()
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
if user is None:
self.access_denied("unknown")
return
server_id = self.get_argument("id")
if (
not user_obj["user_id"]
in self.controller.server_perms.get_server_user_list(server_id)
and not user_obj["superuser"]
):
self.access_denied("unknown")
return
if not self.permissions[
"Commands"
] in self.controller.server_perms.get_api_key_permissions_list(
self.controller.users.get_api_key_by_token(self.api_token), server_id
):
self.access_denied(user)
return
command = self.get_argument("command", default=None, strip=True)
server_id = self.get_argument("id")
if command:
server = self.controller.get_server_obj(server_id)
if server.check_running:
server.send_command(command)
self.return_response(200, {"run": True})
else:
self.return_response(200, {"error": "SER_NOT_RUNNING"})
else:
self.return_response(200, {"error": "NO_COMMAND"})
class ServerBackup(ApiHandler):
def post(self):
user = self.authenticate_user()
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
if user is None:
self.access_denied("unknown")
return
server_id = self.get_argument("id")
if (
not user_obj["user_id"]
in self.controller.server_perms.get_server_user_list(server_id)
and not user_obj["superuser"]
):
self.access_denied("unknown")
return
if not self.permissions[
"Backup"
] in self.controller.server_perms.get_api_key_permissions_list(
self.controller.users.get_api_key_by_token(self.api_token), server_id
):
self.access_denied(user)
return
server = self.controller.get_server_obj(server_id)
server.backup_server()
self.return_response(200, {"code": "SER_BAK_CALLED"})
class StartServer(ApiHandler):
def post(self):
user = self.authenticate_user()
remote_ip = self.get_remote_ip()
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
if user is None:
self.access_denied("unknown")
return
server_id = self.get_argument("id")
if (
not user_obj["user_id"]
in self.controller.server_perms.get_server_user_list(server_id)
and not user_obj["superuser"]
):
self.access_denied("unknown")
return
elif not self.permissions[
"Commands"
] in self.controller.server_perms.get_api_key_permissions_list(
self.controller.users.get_api_key_by_token(self.api_token), server_id
):
self.access_denied("unknown")
return
server = self.controller.get_server_obj(server_id)
if not server.check_running():
self.controller.management.send_command(
user_obj["user_id"], server_id, remote_ip, "start_server"
)
self.return_response(200, {"code": "SER_START_CALLED"})
else:
self.return_response(500, {"error": "SER_RUNNING"})
class StopServer(ApiHandler):
def post(self):
user = self.authenticate_user()
remote_ip = self.get_remote_ip()
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
if user is None:
self.access_denied("unknown")
return
server_id = self.get_argument("id")
if (
not user_obj["user_id"]
in self.controller.server_perms.get_server_user_list(server_id)
and not user_obj["superuser"]
):
self.access_denied("unknown")
if not self.permissions[
"Commands"
] in self.controller.server_perms.get_api_key_permissions_list(
self.controller.users.get_api_key_by_token(self.api_token), server_id
):
self.access_denied(user)
return
server = self.controller.get_server_obj(server_id)
if server.check_running():
self.controller.management.send_command(
user, server_id, remote_ip, "stop_server"
)
self.return_response(200, {"code": "SER_STOP_CALLED"})
else:
self.return_response(500, {"error": "SER_NOT_RUNNING"})
class RestartServer(ApiHandler):
def post(self):
user = self.authenticate_user()
remote_ip = self.get_remote_ip()
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
if user is None:
self.access_denied("unknown")
return
server_id = self.get_argument("id")
if not user_obj["user_id"] in self.controller.server_perms.get_server_user_list(
server_id
):
self.access_denied("unknown")
if not self.permissions[
"Commands"
] in self.controller.server_perms.get_api_key_permissions_list(
self.controller.users.get_api_key_by_token(self.api_token), server_id
):
self.access_denied(user)
self.controller.management.send_command(
user, server_id, remote_ip, "restart_server"
)
self.return_response(200, {"code": "SER_RESTART_CALLED"})
class CreateUser(ApiHandler):
def post(self):
user = self.authenticate_user()
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
user_perms = self.controller.crafty_perms.get_crafty_permissions_list(
user_obj["user_id"]
)
if (
not self.permissions["User_Config"] in user_perms
and not user_obj["superuser"]
):
self.access_denied("unknown")
return
if user is None:
self.access_denied("unknown")
return
if not self.permissions[
"User_Config"
] in self.controller.crafty_perms.get_api_key_permissions_list(
self.controller.users.get_api_key_by_token(self.api_token)
):
self.access_denied(user)
return
new_username = self.get_argument("username")
new_pass = self.get_argument("password")
if new_username:
self.controller.users.add_user(
new_username, new_pass, "default@example.com", True, False
)
self.return_response(
200,
{
"code": "COMPLETE",
"username": new_username,
"password": new_pass,
},
)
else:
self.return_response(
500,
{
"error": "MISSING_PARAMS",
"info": "Some paramaters failed validation",
},
)
class DeleteUser(ApiHandler):
def post(self):
user = self.authenticate_user()
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
user_perms = self.controller.crafty_perms.get_crafty_permissions_list(
user_obj["user_id"]
)
if (
not self.permissions["User_Config"] in user_perms
and not user_obj["superuser"]
):
self.access_denied("unknown")
return
if user is None:
self.access_denied("unknown")
return
if not self.permissions[
"User_Config"
] in self.controller.crafty_perms.get_api_key_permissions_list(
self.controller.users.get_api_key_by_token(self.api_token)
):
self.access_denied(user)
return
user_id = self.get_argument("user_id", None, True)
user_to_del = self.controller.users.get_user_by_id(user_id)
if user_to_del["superuser"]:
self.return_response(
500,
{"error": "NOT_ALLOWED", "info": "You cannot delete a super user"},
)
else:
if user_id:
self.controller.users.remove_user(user_id)
self.return_response(200, {"code": "COMPLETED"})
class ListServers(ApiHandler):
def get(self):
user = self.authenticate_user()
user_obj = self.controller.users.get_user_by_api_token(self.api_token)
if user is None:
self.access_denied("unknown")
return
if self.api_token is None:
self.access_denied("unknown")
return
if user_obj["superuser"]:
servers = self.controller.servers.get_all_defined_servers()
servers = [str(i) for i in servers]
else:
servers = self.controller.servers.get_authorized_servers(
user_obj["user_id"]
)
servers = [str(i) for i in servers]
self.return_response(
200,
{
"code": "COMPLETED",
"servers": servers,
},
)

View File

@ -1,17 +1,9 @@
import logging
from typing import Union, List, Optional, Tuple, Dict, Any
import bleach
import tornado.web
from app.classes.models.users import ApiKeys
from app.classes.shared.authentication import authentication
from app.classes.shared.main_controller import Controller
from app.classes.shared.helpers import helper
try:
import tornado.web
import bleach
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
logger = logging.getLogger(__name__)
@ -23,8 +15,9 @@ class BaseHandler(tornado.web.RequestHandler):
# noinspection PyAttributeOutsideInit
def initialize(
self, controller: Controller = None, tasks_manager=None, translator=None
self, helper=None, controller=None, tasks_manager=None, translator=None
):
self.helper = helper
self.controller = controller
self.tasks_manager = tasks_manager
self.translator = translator
@ -42,7 +35,7 @@ class BaseHandler(tornado.web.RequestHandler):
def get_current_user(
self,
) -> Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]:
return authentication.check(self.get_cookie("token"))
return self.controller.authentication.check(self.get_cookie("token"))
def autobleach(self, name, text):
for r in self.redactables:

View File

@ -8,8 +8,7 @@ logger = logging.getLogger(__name__)
class DefaultHandler(BaseHandler):
# Override prepare() instead of get() to cover all possible HTTP methods.
# pylint: disable=arguments-differ
def prepare(self, page=None):
def prepare(self, page=None): # pylint: disable=arguments-differ
if page is not None:
self.set_status(404)
self.render(

View File

@ -1,20 +1,15 @@
import os
import logging
import bleach
import tornado.web
import tornado.escape
from app.classes.models.server_permissions import Enum_Permissions_Server
from app.classes.shared.console import console
from app.classes.shared.helpers import helper
from app.classes.shared.file_helpers import file_helper
from app.classes.models.server_permissions import EnumPermissionsServer
from app.classes.shared.console import Console
from app.classes.shared.helpers import Helpers
from app.classes.shared.file_helpers import FileHelpers
from app.classes.web.base_handler import BaseHandler
try:
import bleach
import tornado.web
import tornado.escape
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
logger = logging.getLogger(__name__)
@ -36,14 +31,14 @@ class FileHandler(BaseHandler):
server_id = self.get_argument("id", None)
permissions = {
"Commands": Enum_Permissions_Server.Commands,
"Terminal": Enum_Permissions_Server.Terminal,
"Logs": Enum_Permissions_Server.Logs,
"Schedule": Enum_Permissions_Server.Schedule,
"Backup": Enum_Permissions_Server.Backup,
"Files": Enum_Permissions_Server.Files,
"Config": Enum_Permissions_Server.Config,
"Players": Enum_Permissions_Server.Players,
"Commands": EnumPermissionsServer.COMMANDS,
"Terminal": EnumPermissionsServer.TERMINAL,
"Logs": EnumPermissionsServer.LOGS,
"Schedule": EnumPermissionsServer.SCHEDULE,
"Backup": EnumPermissionsServer.BACKUP,
"Files": EnumPermissionsServer.FILES,
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
}
user_perms = self.controller.server_perms.get_user_id_permissions_list(
exec_user["user_id"], server_id
@ -54,7 +49,7 @@ class FileHandler(BaseHandler):
if not superuser:
self.redirect("/panel/error?error=Unauthorized access to Files")
return
file_path = helper.get_os_understandable_path(
file_path = Helpers.get_os_understandable_path(
self.get_argument("file_path", None)
)
@ -63,16 +58,16 @@ class FileHandler(BaseHandler):
else:
server_id = bleach.clean(server_id)
if not helper.in_path(
helper.get_os_understandable_path(
if not Helpers.in_path(
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
file_path,
) or not helper.check_file_exists(os.path.abspath(file_path)):
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
logger.warning(
f"Invalid path in get_file file file ajax call ({file_path})"
)
console.warning(
Console.warning(
f"Invalid path in get_file file file ajax call ({file_path})"
)
return
@ -101,13 +96,13 @@ class FileHandler(BaseHandler):
else:
server_id = bleach.clean(server_id)
if helper.validate_traversal(
if Helpers.validate_traversal(
self.controller.servers.get_server_data_by_id(server_id)["path"], path
):
self.write(
helper.get_os_understandable_path(path)
Helpers.get_os_understandable_path(path)
+ "\n"
+ helper.generate_tree(path)
+ Helpers.generate_tree(path)
)
self.finish()
@ -123,13 +118,13 @@ class FileHandler(BaseHandler):
else:
server_id = bleach.clean(server_id)
if helper.validate_traversal(
if Helpers.validate_traversal(
self.controller.servers.get_server_data_by_id(server_id)["path"], path
):
self.write(
helper.get_os_understandable_path(path)
Helpers.get_os_understandable_path(path)
+ "\n"
+ helper.generate_dir(path)
+ Helpers.generate_dir(path)
)
self.finish()
@ -143,14 +138,14 @@ class FileHandler(BaseHandler):
server_id = self.get_argument("id", None)
permissions = {
"Commands": Enum_Permissions_Server.Commands,
"Terminal": Enum_Permissions_Server.Terminal,
"Logs": Enum_Permissions_Server.Logs,
"Schedule": Enum_Permissions_Server.Schedule,
"Backup": Enum_Permissions_Server.Backup,
"Files": Enum_Permissions_Server.Files,
"Config": Enum_Permissions_Server.Config,
"Players": Enum_Permissions_Server.Players,
"Commands": EnumPermissionsServer.COMMANDS,
"Terminal": EnumPermissionsServer.TERMINAL,
"Logs": EnumPermissionsServer.LOGS,
"Schedule": EnumPermissionsServer.SCHEDULE,
"Backup": EnumPermissionsServer.BACKUP,
"Files": EnumPermissionsServer.FILES,
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
}
user_perms = self.controller.server_perms.get_user_id_permissions_list(
exec_user["user_id"], server_id
@ -161,7 +156,7 @@ class FileHandler(BaseHandler):
if not superuser:
self.redirect("/panel/error?error=Unauthorized access to Files")
return
file_parent = helper.get_os_understandable_path(
file_parent = Helpers.get_os_understandable_path(
self.get_body_argument("file_parent", default=None, strip=True)
)
file_name = self.get_body_argument("file_name", default=None, strip=True)
@ -172,16 +167,16 @@ class FileHandler(BaseHandler):
else:
server_id = bleach.clean(server_id)
if not helper.in_path(
helper.get_os_understandable_path(
if not Helpers.in_path(
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
file_path,
) or helper.check_file_exists(os.path.abspath(file_path)):
) or Helpers.check_file_exists(os.path.abspath(file_path)):
logger.warning(
f"Invalid path in create_file file ajax call ({file_path})"
)
console.warning(
Console.warning(
f"Invalid path in create_file file ajax call ({file_path})"
)
return
@ -195,7 +190,7 @@ class FileHandler(BaseHandler):
if not superuser:
self.redirect("/panel/error?error=Unauthorized access to Files")
return
dir_parent = helper.get_os_understandable_path(
dir_parent = Helpers.get_os_understandable_path(
self.get_body_argument("dir_parent", default=None, strip=True)
)
dir_name = self.get_body_argument("dir_name", default=None, strip=True)
@ -206,16 +201,16 @@ class FileHandler(BaseHandler):
else:
server_id = bleach.clean(server_id)
if not helper.in_path(
helper.get_os_understandable_path(
if not Helpers.in_path(
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
dir_path,
) or helper.check_path_exists(os.path.abspath(dir_path)):
) or Helpers.check_path_exists(os.path.abspath(dir_path)):
logger.warning(
f"Invalid path in create_dir file ajax call ({dir_path})"
)
console.warning(
Console.warning(
f"Invalid path in create_dir file ajax call ({dir_path})"
)
return
@ -227,8 +222,8 @@ class FileHandler(BaseHandler):
if not superuser:
self.redirect("/panel/error?error=Unauthorized access to Files")
return
path = helper.get_os_understandable_path(self.get_argument("path", None))
helper.unzipFile(path)
path = Helpers.get_os_understandable_path(self.get_argument("path", None))
Helpers.unzip_file(path)
self.redirect(f"/panel/server_detail?id={server_id}&subpage=files")
return
@ -242,14 +237,14 @@ class FileHandler(BaseHandler):
server_id = self.get_argument("id", None)
permissions = {
"Commands": Enum_Permissions_Server.Commands,
"Terminal": Enum_Permissions_Server.Terminal,
"Logs": Enum_Permissions_Server.Logs,
"Schedule": Enum_Permissions_Server.Schedule,
"Backup": Enum_Permissions_Server.Backup,
"Files": Enum_Permissions_Server.Files,
"Config": Enum_Permissions_Server.Config,
"Players": Enum_Permissions_Server.Players,
"Commands": EnumPermissionsServer.COMMANDS,
"Terminal": EnumPermissionsServer.TERMINAL,
"Logs": EnumPermissionsServer.LOGS,
"Schedule": EnumPermissionsServer.SCHEDULE,
"Backup": EnumPermissionsServer.BACKUP,
"Files": EnumPermissionsServer.FILES,
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
}
user_perms = self.controller.server_perms.get_user_id_permissions_list(
exec_user["user_id"], server_id
@ -259,11 +254,11 @@ class FileHandler(BaseHandler):
if not superuser:
self.redirect("/panel/error?error=Unauthorized access to Files")
return
file_path = helper.get_os_understandable_path(
file_path = Helpers.get_os_understandable_path(
self.get_body_argument("file_path", default=None, strip=True)
)
console.warning(f"Delete {file_path} for server {server_id}")
Console.warning(f"Delete {file_path} for server {server_id}")
if not self.check_server_id(server_id, "del_file"):
return
@ -272,33 +267,33 @@ class FileHandler(BaseHandler):
server_info = self.controller.servers.get_server_data_by_id(server_id)
if not (
helper.in_path(
helper.get_os_understandable_path(server_info["path"]), file_path
Helpers.in_path(
Helpers.get_os_understandable_path(server_info["path"]), file_path
)
or helper.in_path(
helper.get_os_understandable_path(server_info["backup_path"]),
or Helpers.in_path(
Helpers.get_os_understandable_path(server_info["backup_path"]),
file_path,
)
) or not helper.check_file_exists(os.path.abspath(file_path)):
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
logger.warning(f"Invalid path in del_file file ajax call ({file_path})")
console.warning(
Console.warning(
f"Invalid path in del_file file ajax call ({file_path})"
)
return
# Delete the file
file_helper.del_file(file_path)
FileHelpers.del_file(file_path)
elif page == "del_dir":
if not permissions["Files"] in user_perms:
if not superuser:
self.redirect("/panel/error?error=Unauthorized access to Files")
return
dir_path = helper.get_os_understandable_path(
dir_path = Helpers.get_os_understandable_path(
self.get_body_argument("dir_path", default=None, strip=True)
)
console.warning(f"Delete {dir_path} for server {server_id}")
Console.warning(f"Delete {dir_path} for server {server_id}")
if not self.check_server_id(server_id, "del_dir"):
return
@ -306,20 +301,20 @@ class FileHandler(BaseHandler):
server_id = bleach.clean(server_id)
server_info = self.controller.servers.get_server_data_by_id(server_id)
if not helper.in_path(
helper.get_os_understandable_path(server_info["path"]), dir_path
) or not helper.check_path_exists(os.path.abspath(dir_path)):
if not Helpers.in_path(
Helpers.get_os_understandable_path(server_info["path"]), dir_path
) or not Helpers.check_path_exists(os.path.abspath(dir_path)):
logger.warning(f"Invalid path in del_file file ajax call ({dir_path})")
console.warning(f"Invalid path in del_file file ajax call ({dir_path})")
Console.warning(f"Invalid path in del_file file ajax call ({dir_path})")
return
# Delete the directory
# os.rmdir(dir_path) # Would only remove empty directories
if helper.validate_traversal(
helper.get_os_understandable_path(server_info["path"]), dir_path
if Helpers.validate_traversal(
Helpers.get_os_understandable_path(server_info["path"]), dir_path
):
# Removes also when there are contents
file_helper.del_dirs(dir_path)
FileHelpers.del_dirs(dir_path)
@tornado.web.authenticated
def put(self, page):
@ -330,14 +325,14 @@ class FileHandler(BaseHandler):
server_id = self.get_argument("id", None)
permissions = {
"Commands": Enum_Permissions_Server.Commands,
"Terminal": Enum_Permissions_Server.Terminal,
"Logs": Enum_Permissions_Server.Logs,
"Schedule": Enum_Permissions_Server.Schedule,
"Backup": Enum_Permissions_Server.Backup,
"Files": Enum_Permissions_Server.Files,
"Config": Enum_Permissions_Server.Config,
"Players": Enum_Permissions_Server.Players,
"Commands": EnumPermissionsServer.COMMANDS,
"Terminal": EnumPermissionsServer.TERMINAL,
"Logs": EnumPermissionsServer.LOGS,
"Schedule": EnumPermissionsServer.SCHEDULE,
"Backup": EnumPermissionsServer.BACKUP,
"Files": EnumPermissionsServer.FILES,
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
}
user_perms = self.controller.server_perms.get_user_id_permissions_list(
exec_user["user_id"], server_id
@ -350,7 +345,7 @@ class FileHandler(BaseHandler):
file_contents = self.get_body_argument(
"file_contents", default=None, strip=True
)
file_path = helper.get_os_understandable_path(
file_path = Helpers.get_os_understandable_path(
self.get_body_argument("file_path", default=None, strip=True)
)
@ -359,16 +354,16 @@ class FileHandler(BaseHandler):
else:
server_id = bleach.clean(server_id)
if not helper.in_path(
helper.get_os_understandable_path(
if not Helpers.in_path(
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
file_path,
) or not helper.check_file_exists(os.path.abspath(file_path)):
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
logger.warning(
f"Invalid path in save_file file ajax call ({file_path})"
)
console.warning(
Console.warning(
f"Invalid path in save_file file ajax call ({file_path})"
)
return
@ -382,7 +377,7 @@ class FileHandler(BaseHandler):
if not superuser:
self.redirect("/panel/error?error=Unauthorized access to Files")
return
item_path = helper.get_os_understandable_path(
item_path = Helpers.get_os_understandable_path(
self.get_body_argument("item_path", default=None, strip=True)
)
new_item_name = self.get_body_argument(
@ -396,35 +391,35 @@ class FileHandler(BaseHandler):
if item_path is None or new_item_name is None:
logger.warning("Invalid path(s) in rename_file file ajax call")
console.warning("Invalid path(s) in rename_file file ajax call")
Console.warning("Invalid path(s) in rename_file file ajax call")
return
if not helper.in_path(
helper.get_os_understandable_path(
if not Helpers.in_path(
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
item_path,
) or not helper.check_path_exists(os.path.abspath(item_path)):
) or not Helpers.check_path_exists(os.path.abspath(item_path)):
logger.warning(
f"Invalid old name path in rename_file file ajax call ({server_id})"
)
console.warning(
Console.warning(
f"Invalid old name path in rename_file file ajax call ({server_id})"
)
return
new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name)
if not helper.in_path(
helper.get_os_understandable_path(
if not Helpers.in_path(
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
new_item_path,
) or helper.check_path_exists(os.path.abspath(new_item_path)):
) or Helpers.check_path_exists(os.path.abspath(new_item_path)):
logger.warning(
f"Invalid new name path in rename_file file ajax call ({server_id})"
)
console.warning(
Console.warning(
f"Invalid new name path in rename_file file ajax call ({server_id})"
)
return
@ -441,14 +436,14 @@ class FileHandler(BaseHandler):
server_id = self.get_argument("id", None)
permissions = {
"Commands": Enum_Permissions_Server.Commands,
"Terminal": Enum_Permissions_Server.Terminal,
"Logs": Enum_Permissions_Server.Logs,
"Schedule": Enum_Permissions_Server.Schedule,
"Backup": Enum_Permissions_Server.Backup,
"Files": Enum_Permissions_Server.Files,
"Config": Enum_Permissions_Server.Config,
"Players": Enum_Permissions_Server.Players,
"Commands": EnumPermissionsServer.COMMANDS,
"Terminal": EnumPermissionsServer.TERMINAL,
"Logs": EnumPermissionsServer.LOGS,
"Schedule": EnumPermissionsServer.SCHEDULE,
"Backup": EnumPermissionsServer.BACKUP,
"Files": EnumPermissionsServer.FILES,
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
}
user_perms = self.controller.server_perms.get_user_id_permissions_list(
exec_user["user_id"], server_id
@ -458,7 +453,7 @@ class FileHandler(BaseHandler):
if not superuser:
self.redirect("/panel/error?error=Unauthorized access to Files")
return
item_path = helper.get_os_understandable_path(
item_path = Helpers.get_os_understandable_path(
self.get_body_argument("item_path", default=None, strip=True)
)
new_item_name = self.get_body_argument(
@ -472,35 +467,35 @@ class FileHandler(BaseHandler):
if item_path is None or new_item_name is None:
logger.warning("Invalid path(s) in rename_file file ajax call")
console.warning("Invalid path(s) in rename_file file ajax call")
Console.warning("Invalid path(s) in rename_file file ajax call")
return
if not helper.in_path(
helper.get_os_understandable_path(
if not Helpers.in_path(
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
item_path,
) or not helper.check_path_exists(os.path.abspath(item_path)):
) or not Helpers.check_path_exists(os.path.abspath(item_path)):
logger.warning(
f"Invalid old name path in rename_file file ajax call ({server_id})"
)
console.warning(
Console.warning(
f"Invalid old name path in rename_file file ajax call ({server_id})"
)
return
new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name)
if not helper.in_path(
helper.get_os_understandable_path(
if not Helpers.in_path(
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
new_item_path,
) or helper.check_path_exists(os.path.abspath(new_item_path)):
) or Helpers.check_path_exists(os.path.abspath(new_item_path)):
logger.warning(
f"Invalid new name path in rename_file file ajax call ({server_id})"
)
console.warning(
Console.warning(
f"Invalid new name path in rename_file file ajax call ({server_id})"
)
return
@ -513,7 +508,7 @@ class FileHandler(BaseHandler):
logger.warning(
f"Server ID not defined in {page_name} file ajax call ({server_id})"
)
console.warning(
Console.warning(
f"Server ID not defined in {page_name} file ajax call ({server_id})"
)
return
@ -525,7 +520,7 @@ class FileHandler(BaseHandler):
logger.warning(
f"Server ID not found in {page_name} file ajax call ({server_id})"
)
console.warning(
Console.warning(
f"Server ID not found in {page_name} file ajax call ({server_id})"
)
return

View File

@ -1,14 +1,8 @@
import logging
import requests
from app.classes.shared.helpers import helper
from app.classes.web.base_handler import BaseHandler
try:
import requests
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
logger = logging.getLogger(__name__)
@ -21,7 +15,7 @@ class HTTPHandler(BaseHandler):
url = "https://" + url_list[0]
else:
url = "https://" + url
db_port = helper.get_setting("https_port")
db_port = self.helper.get_setting("https_port")
try:
resp = requests.get(url + ":" + str(port))
resp.raise_for_status()
@ -39,7 +33,7 @@ class HTTPHandlerPage(BaseHandler):
url = "https://" + url_list[0]
else:
url = "https://" + url
db_port = helper.get_setting("https_port")
db_port = self.helper.get_setting("https_port")
try:
resp = requests.get(url + ":" + str(port))
resp.raise_for_status()

View File

@ -1,7 +1,5 @@
import logging
import requests
from app.classes.shared.helpers import helper
from app.classes.web.base_handler import BaseHandler
logger = logging.getLogger(__name__)
@ -16,13 +14,15 @@ class HTTPHandlerPage(BaseHandler):
url_list = url.split("/")
if url_list[0] != "":
primary_url = url_list[0] + ":" + str(port) + "/"
backup_url = url_list[0] + ":" + str(helper.get_setting("https_port")) + "/"
backup_url = (
url_list[0] + ":" + str(self.helper.get_setting("https_port")) + "/"
)
for i in range(len(url_list) - 1):
primary_url += url_list[i + 1]
backup_url += url_list[i + 1]
else:
primary_url = url + str(port)
backup_url = url + str(helper.get_setting("https_port"))
backup_url = url + str(self.helper.get_setting("https_port"))
try:
resp = requests.get(primary_url)

View File

@ -6,29 +6,23 @@ from typing import Dict, Any, Tuple
import json
import logging
import threading
import bleach
import libgravatar
import requests
import tornado.web
import tornado.escape
from tornado import iostream
from app.classes.models.server_permissions import Enum_Permissions_Server
from app.classes.models.crafty_permissions import Enum_Permissions_Crafty
from app.classes.models.management import management_helper
from app.classes.shared.authentication import authentication
from app.classes.shared.helpers import helper
# TZLocal is set as a hidden import on win pipeline
from tzlocal import get_localzone
from cron_validator import CronValidator
from app.classes.models.server_permissions import EnumPermissionsServer
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
from app.classes.models.management import HelpersManagement
from app.classes.shared.helpers import Helpers
from app.classes.web.base_handler import BaseHandler
try:
import bleach
import libgravatar
import requests
import tornado.web
import tornado.escape
from tornado import iostream
# TZLocal is set as a hidden import on win pipeline
from tzlocal import get_localzone
from cron_validator import CronValidator
except ModuleNotFoundError as ex:
helper.auto_installer_fix(ex)
logger = logging.getLogger(__name__)
@ -191,10 +185,10 @@ class PanelHandler(BaseHandler):
)
page_data["num_players"] = total_players
for s in page_data["servers"]:
for server in page_data["servers"]:
try:
data = json.loads(s["int_ping_results"])
s["int_ping_results"] = data
data = json.loads(server["int_ping_results"])
server["int_ping_results"] = data
except Exception as e:
logger.error(f"Failed server data for page with error: {e}")
@ -268,14 +262,14 @@ class PanelHandler(BaseHandler):
# todo: make this actually pull and compare version data
"update_available": False,
"serverTZ": get_localzone(),
"version_data": helper.get_version_string(),
"version_data": self.helper.get_version_string(),
"user_data": exec_user,
"user_role": exec_user_role,
"user_crafty_permissions": exec_user_crafty_permissions,
"crafty_permissions": {
"Server_Creation": Enum_Permissions_Crafty.Server_Creation,
"User_Config": Enum_Permissions_Crafty.User_Config,
"Roles_Config": Enum_Permissions_Crafty.Roles_Config,
"Server_Creation": EnumPermissionsCrafty.SERVER_CREATION,
"User_Config": EnumPermissionsCrafty.USER_CONFIG,
"Roles_Config": EnumPermissionsCrafty.ROLES_CONFIG,
},
"server_stats": {
"total": len(defined_servers),
@ -287,11 +281,11 @@ class PanelHandler(BaseHandler):
},
"menu_servers": defined_servers,
"hosts_data": self.controller.management.get_latest_hosts_stats(),
"show_contribute": helper.get_setting("show_contribute_link", True),
"show_contribute": self.helper.get_setting("show_contribute_link", True),
"error": error,
"time": formatted_time,
"lang": self.controller.users.get_user_lang_by_id(exec_user["user_id"]),
"lang_page": helper.getLangPage(
"lang_page": Helpers.get_lang_page(
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
),
"super_user": superuser,
@ -306,15 +300,17 @@ class PanelHandler(BaseHandler):
else None,
"superuser": superuser,
}
if helper.get_setting("allow_nsfw_profile_pictures"):
if self.helper.get_setting("allow_nsfw_profile_pictures"):
rating = "x"
else:
rating = "g"
# Get grvatar hash for profile pictures
if exec_user["email"] != "default@example.com" or "":
g = libgravatar.Gravatar(libgravatar.sanitize_email(exec_user["email"]))
url = g.get_image(
gravatar = libgravatar.Gravatar(
libgravatar.sanitize_email(exec_user["email"])
)
url = gravatar.get_image(
size=80,
default="404",
force_default=False,
@ -322,9 +318,12 @@ class PanelHandler(BaseHandler):
filetype_extension=False,
use_ssl=True,
) # + "?d=404"
if requests.head(url).status_code != 404:
profile_url = url
else:
try:
if requests.head(url).status_code != 404:
profile_url = url
else:
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
except:
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
else:
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
@ -338,7 +337,9 @@ class PanelHandler(BaseHandler):
template = "public/error.html"
elif page == "credits":
with open(helper.credits_cache, encoding="utf-8") as credits_default_local:
with open(
self.helper.credits_cache, encoding="utf-8"
) as credits_default_local:
try:
remote = requests.get(
"https://craftycontrol.com/credits", allow_redirects=True
@ -499,14 +500,14 @@ class PanelHandler(BaseHandler):
)
page_data["active_link"] = subpage
page_data["permissions"] = {
"Commands": Enum_Permissions_Server.Commands,
"Terminal": Enum_Permissions_Server.Terminal,
"Logs": Enum_Permissions_Server.Logs,
"Schedule": Enum_Permissions_Server.Schedule,
"Backup": Enum_Permissions_Server.Backup,
"Files": Enum_Permissions_Server.Files,
"Config": Enum_Permissions_Server.Config,
"Players": Enum_Permissions_Server.Players,
"Commands": EnumPermissionsServer.COMMANDS,
"Terminal": EnumPermissionsServer.TERMINAL,
"Logs": EnumPermissionsServer.LOGS,
"Schedule": EnumPermissionsServer.SCHEDULE,
"Backup": EnumPermissionsServer.BACKUP,
"Files": EnumPermissionsServer.FILES,
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
}
page_data[
"user_permissions"
@ -582,7 +583,7 @@ class PanelHandler(BaseHandler):
"/panel/error?error=Unauthorized access To Schedules"
)
return
page_data["schedules"] = management_helper.get_schedules_by_server(
page_data["schedules"] = HelpersManagement.get_schedules_by_server(
server_id
)
@ -632,7 +633,7 @@ class PanelHandler(BaseHandler):
).send_backup_status()
# makes it so relative path is the only thing shown
for file in page_data["exclusions"]:
if helper.is_os_windows():
if Helpers.is_os_windows():
exclusions.append(file.replace(server_info["path"] + "\\", ""))
else:
exclusions.append(file.replace(server_info["path"] + "/", ""))
@ -642,7 +643,7 @@ class PanelHandler(BaseHandler):
page_data["backup_list"] = server.list_backups()
except:
page_data["backup_list"] = []
page_data["backup_path"] = helper.wtol_path(server_info["backup_path"])
page_data["backup_path"] = Helpers.wtol_path(server_info["backup_path"])
def get_banned_players_html():
banned_players = self.controller.servers.get_banned_players(server_id)
@ -685,11 +686,11 @@ class PanelHandler(BaseHandler):
server_info = self.controller.servers.get_server_data_by_id(server_id)
backup_file = os.path.abspath(
os.path.join(
helper.get_os_understandable_path(server_info["backup_path"]), file
Helpers.get_os_understandable_path(server_info["backup_path"]), file
)
)
if not helper.in_path(
helper.get_os_understandable_path(server_info["backup_path"]),
if not Helpers.in_path(
Helpers.get_os_understandable_path(server_info["backup_path"]),
backup_file,
) or not os.path.isfile(backup_file):
self.redirect("/panel/error?error=Invalid path detected")
@ -766,8 +767,9 @@ class PanelHandler(BaseHandler):
page_data["user"]["last_ip"] = "N/A"
page_data["user"]["last_update"] = "N/A"
page_data["user"]["roles"] = set()
page_data["user"]["hints"] = True
if Enum_Permissions_Crafty.User_Config not in exec_user_crafty_permissions:
if EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions:
self.redirect(
"/panel/error?error=Unauthorized access: not a user editor"
)
@ -795,10 +797,10 @@ class PanelHandler(BaseHandler):
else:
page_data["super-disabled"] = "disabled"
for file in sorted(
os.listdir(os.path.join(helper.root_dir, "app", "translations"))
os.listdir(os.path.join(self.helper.root_dir, "app", "translations"))
):
if file.endswith(".json"):
if file not in helper.get_setting("disabled_language_files"):
if file not in self.helper.get_setting("disabled_language_files"):
if file != str(page_data["languages"][0] + ".json"):
page_data["languages"].append(file.split(".")[0])
@ -806,7 +808,7 @@ class PanelHandler(BaseHandler):
elif page == "add_schedule":
server_id = self.get_argument("id", None)
page_data["schedules"] = management_helper.get_schedules_by_server(
page_data["schedules"] = HelpersManagement.get_schedules_by_server(
server_id
)
page_data["get_players"] = lambda: self.controller.stats.get_server_players(
@ -814,14 +816,14 @@ class PanelHandler(BaseHandler):
)
page_data["active_link"] = "schedules"
page_data["permissions"] = {
"Commands": Enum_Permissions_Server.Commands,
"Terminal": Enum_Permissions_Server.Terminal,
"Logs": Enum_Permissions_Server.Logs,
"Schedule": Enum_Permissions_Server.Schedule,
"Backup": Enum_Permissions_Server.Backup,
"Files": Enum_Permissions_Server.Files,
"Config": Enum_Permissions_Server.Config,
"Players": Enum_Permissions_Server.Players,
"Commands": EnumPermissionsServer.COMMANDS,
"Terminal": EnumPermissionsServer.TERMINAL,
"Logs": EnumPermissionsServer.LOGS,
"Schedule": EnumPermissionsServer.SCHEDULE,
"Backup": EnumPermissionsServer.BACKUP,
"Files": EnumPermissionsServer.FILES,
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
}
page_data[
"user_permissions"
@ -854,7 +856,7 @@ class PanelHandler(BaseHandler):
page_data["schedule"]["difficulty"] = "basic"
page_data["schedule"]["interval_type"] = "days"
if not Enum_Permissions_Server.Schedule in page_data["user_permissions"]:
if not EnumPermissionsServer.SCHEDULE in page_data["user_permissions"]:
if not superuser:
self.redirect("/panel/error?error=Unauthorized access To Schedules")
return
@ -863,7 +865,7 @@ class PanelHandler(BaseHandler):
elif page == "edit_schedule":
server_id = self.get_argument("id", None)
page_data["schedules"] = management_helper.get_schedules_by_server(
page_data["schedules"] = HelpersManagement.get_schedules_by_server(
server_id
)
sch_id = self.get_argument("sch_id", None)
@ -873,14 +875,14 @@ class PanelHandler(BaseHandler):
)
page_data["active_link"] = "schedules"
page_data["permissions"] = {
"Commands": Enum_Permissions_Server.Commands,
"Terminal": Enum_Permissions_Server.Terminal,
"Logs": Enum_Permissions_Server.Logs,
"Schedule": Enum_Permissions_Server.Schedule,
"Backup": Enum_Permissions_Server.Backup,
"Files": Enum_Permissions_Server.Files,
"Config": Enum_Permissions_Server.Config,
"Players": Enum_Permissions_Server.Players,
"Commands": EnumPermissionsServer.COMMANDS,
"Terminal": EnumPermissionsServer.TERMINAL,
"Logs": EnumPermissionsServer.LOGS,
"Schedule": EnumPermissionsServer.SCHEDULE,
"Backup": EnumPermissionsServer.BACKUP,
"Files": EnumPermissionsServer.FILES,
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
}
page_data[
"user_permissions"
@ -933,7 +935,7 @@ class PanelHandler(BaseHandler):
if sch_id is None or server_id is None:
self.redirect("/panel/error?error=Invalid server ID or Schedule ID")
if not Enum_Permissions_Server.Schedule in page_data["user_permissions"]:
if not EnumPermissionsServer.SCHEDULE in page_data["user_permissions"]:
if not superuser:
self.redirect("/panel/error?error=Unauthorized access To Schedules")
return
@ -975,19 +977,17 @@ class PanelHandler(BaseHandler):
page_data["super-disabled"] = "disabled"
for file in sorted(
os.listdir(os.path.join(helper.root_dir, "app", "translations"))
os.listdir(os.path.join(self.helper.root_dir, "app", "translations"))
):
if file.endswith(".json"):
if file not in helper.get_setting("disabled_language_files"):
if file not in self.helper.get_setting("disabled_language_files"):
if file != str(page_data["languages"][0] + ".json"):
page_data["languages"].append(file.split(".")[0])
if user_id is None:
self.redirect("/panel/error?error=Invalid User ID")
return
elif (
Enum_Permissions_Crafty.User_Config not in exec_user_crafty_permissions
):
elif EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions:
if str(user_id) != str(exec_user["user_id"]):
self.redirect(
"/panel/error?error=Unauthorized access: not a user editor"
@ -1029,7 +1029,7 @@ class PanelHandler(BaseHandler):
if (
not superuser
and Enum_Permissions_Crafty.User_Config
and EnumPermissionsCrafty.USER_CONFIG
not in exec_user_crafty_permissions
):
self.redirect("/panel/error?error=Unauthorized access: not superuser")
@ -1075,7 +1075,7 @@ class PanelHandler(BaseHandler):
page_data["user-roles"] = user_roles
page_data["users"] = self.controller.users.get_all_users()
if Enum_Permissions_Crafty.Roles_Config not in exec_user_crafty_permissions:
if EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions:
self.redirect(
"/panel/error?error=Unauthorized access: not a role editor"
)
@ -1103,7 +1103,7 @@ class PanelHandler(BaseHandler):
page_data["user-roles"] = user_roles
page_data["users"] = self.controller.users.get_all_users()
if Enum_Permissions_Crafty.Roles_Config not in exec_user_crafty_permissions:
if EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions:
self.redirect(
"/panel/error?error=Unauthorized access: not a role editor"
)
@ -1146,7 +1146,7 @@ class PanelHandler(BaseHandler):
template = "panel/activity_logs.html"
elif page == "download_file":
file = helper.get_os_understandable_path(self.get_argument("path", ""))
file = Helpers.get_os_understandable_path(self.get_argument("path", ""))
name = self.get_argument("name", "")
server_id = self.check_server_id()
@ -1155,8 +1155,8 @@ class PanelHandler(BaseHandler):
server_info = self.controller.servers.get_server_data_by_id(server_id)
if not helper.in_path(
helper.get_os_understandable_path(server_info["path"]), file
if not Helpers.in_path(
Helpers.get_os_understandable_path(server_info["path"]), file
) or not os.path.isfile(file):
self.redirect("/panel/error?error=Invalid path detected")
return
@ -1165,7 +1165,7 @@ class PanelHandler(BaseHandler):
self.redirect(f"/panel/server_detail?id={server_id}&subpage=files")
elif page == "download_support_package":
tempZipStorage = exec_user["support_logs"]
temp_zip_storage = exec_user["support_logs"]
# We'll reset the support path for this user now.
self.controller.users.set_support_path(exec_user["user_id"], "")
@ -1174,8 +1174,8 @@ class PanelHandler(BaseHandler):
"Content-Disposition", "attachment; filename=" + "support_logs.zip"
)
chunk_size = 1024 * 1024 * 4 # 4 MiB
if tempZipStorage != "":
with open(tempZipStorage, "rb") as f:
if temp_zip_storage != "":
with open(temp_zip_storage, "rb") as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
@ -1230,14 +1230,14 @@ class PanelHandler(BaseHandler):
server_id = self.get_argument("id", None)
permissions = {
"Commands": Enum_Permissions_Server.Commands,
"Terminal": Enum_Permissions_Server.Terminal,
"Logs": Enum_Permissions_Server.Logs,
"Schedule": Enum_Permissions_Server.Schedule,
"Backup": Enum_Permissions_Server.Backup,
"Files": Enum_Permissions_Server.Files,
"Config": Enum_Permissions_Server.Config,
"Players": Enum_Permissions_Server.Players,
"Commands": EnumPermissionsServer.COMMANDS,
"Terminal": EnumPermissionsServer.TERMINAL,
"Logs": EnumPermissionsServer.LOGS,
"Schedule": EnumPermissionsServer.SCHEDULE,
"Backup": EnumPermissionsServer.BACKUP,
"Files": EnumPermissionsServer.FILES,
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
}
exec_user_role = set()
if superuser:
@ -1271,13 +1271,13 @@ class PanelHandler(BaseHandler):
server_obj = self.controller.servers.get_server_obj(server_id)
if superuser:
server_path = self.get_argument("server_path", None)
if helper.is_os_windows():
if Helpers.is_os_windows():
server_path.replace(" ", "^ ")
server_path = helper.wtol_path(server_path)
server_path = Helpers.wtol_path(server_path)
log_path = self.get_argument("log_path", None)
if helper.is_os_windows():
if Helpers.is_os_windows():
log_path.replace(" ", "^ ")
log_path = helper.wtol_path(log_path)
log_path = Helpers.wtol_path(log_path)
executable = self.get_argument("executable", None)
execution_command = self.get_argument("execution_command", None)
server_ip = self.get_argument("server_ip", None)
@ -1308,12 +1308,14 @@ class PanelHandler(BaseHandler):
server_obj.server_name = server_name
if superuser:
if helper.validate_traversal(
helper.get_servers_root_dir(), server_path
if Helpers.validate_traversal(
self.helper.get_servers_root_dir(), server_path
):
server_obj.path = server_path
server_obj.log_path = log_path
if helper.validate_traversal(helper.get_servers_root_dir(), executable):
if Helpers.validate_traversal(
self.helper.get_servers_root_dir(), executable
):
server_obj.executable = executable
server_obj.execution_command = execution_command
server_obj.server_ip = server_ip
@ -1358,9 +1360,9 @@ class PanelHandler(BaseHandler):
checked = self.controller.management.get_excluded_backup_dirs(server_id)
if superuser:
backup_path = bleach.clean(self.get_argument("backup_path", None))
if helper.is_os_windows():
if Helpers.is_os_windows():
backup_path.replace(" ", "^ ")
backup_path = helper.wtol_path(backup_path)
backup_path = Helpers.wtol_path(backup_path)
else:
backup_path = server_obj.backup_path
max_backups = bleach.clean(self.get_argument("max_backups", None))
@ -1473,7 +1475,7 @@ class PanelHandler(BaseHandler):
one_time = False
if not superuser and not permissions[
"Backup"
"Schedule"
] in self.controller.server_perms.get_user_id_permissions_list(
exec_user["user_id"], server_id
):
@ -1631,7 +1633,7 @@ class PanelHandler(BaseHandler):
one_time = False
if not superuser and not permissions[
"Backup"
"Schedule"
] in self.controller.server_perms.get_user_id_permissions_list(
exec_user["user_id"], server_id
):
@ -1731,8 +1733,13 @@ class PanelHandler(BaseHandler):
password1 = bleach.clean(self.get_argument("password1", None))
email = bleach.clean(self.get_argument("email", "default@example.com"))
enabled = int(float(self.get_argument("enabled", "0")))
try:
hints = int(bleach.clean(self.get_argument("hints")))
hints = True
except:
hints = False
lang = bleach.clean(
self.get_argument("language"), helper.get_setting("language")
self.get_argument("language"), self.helper.get_setting("language")
)
if superuser:
@ -1749,67 +1756,73 @@ class PanelHandler(BaseHandler):
superuser = True
else:
superuser = False
if not exec_user["superuser"]:
if (
EnumPermissionsCrafty.USER_CONFIG
not in exec_user_crafty_permissions
):
if str(user_id) != str(exec_user["user_id"]):
self.redirect(
"/panel/error?error=Unauthorized access: not a user editor"
)
return
if Enum_Permissions_Crafty.User_Config not in exec_user_crafty_permissions:
if str(user_id) != str(exec_user["user_id"]):
self.redirect(
"/panel/error?error=Unauthorized access: not a user editor"
user_data = {
"username": username,
"password": password0,
"email": email,
"lang": lang,
"hints": hints,
}
self.controller.users.update_user(user_id, user_data=user_data)
self.controller.management.add_to_audit_log(
exec_user["user_id"],
f"Edited user {username} (UID:{user_id}) password",
server_id=0,
source_ip=self.get_remote_ip(),
)
self.redirect("/panel/panel_config")
return
elif username is None or username == "":
self.redirect("/panel/error?error=Invalid username")
return
elif user_id is None:
self.redirect("/panel/error?error=Invalid User ID")
return
else:
# does this user id exist?
if not self.controller.users.user_id_exists(user_id):
self.redirect("/panel/error?error=Invalid User ID")
return
else:
if password0 != password1:
self.redirect("/panel/error?error=Passwords must match")
return
roles = self.get_user_role_memberships()
permissions_mask, server_quantity = self.get_perms_quantity()
# if email is None or "":
# email = "default@example.com"
user_data = {
"username": username,
"password": password0,
"email": email,
"enabled": enabled,
"roles": roles,
"lang": lang,
"superuser": superuser,
"hints": hints,
}
self.controller.users.update_user(user_id, user_data=user_data)
self.controller.management.add_to_audit_log(
exec_user["user_id"],
f"Edited user {username} (UID:{user_id}) password",
server_id=0,
source_ip=self.get_remote_ip(),
user_crafty_data = {
"permissions_mask": permissions_mask,
"server_quantity": server_quantity,
}
self.controller.users.update_user(
user_id, user_data=user_data, user_crafty_data=user_crafty_data
)
self.redirect("/panel/panel_config")
return
elif username is None or username == "":
self.redirect("/panel/error?error=Invalid username")
return
elif user_id is None:
self.redirect("/panel/error?error=Invalid User ID")
return
else:
# does this user id exist?
if not self.controller.users.user_id_exists(user_id):
self.redirect("/panel/error?error=Invalid User ID")
return
if password0 != password1:
self.redirect("/panel/error?error=Passwords must match")
return
roles = self.get_user_role_memberships()
permissions_mask, server_quantity = self.get_perms_quantity()
# if email is None or "":
# email = "default@example.com"
user_data = {
"username": username,
"password": password0,
"email": email,
"enabled": enabled,
"roles": roles,
"lang": lang,
"superuser": superuser,
}
user_crafty_data = {
"permissions_mask": permissions_mask,
"server_quantity": server_quantity,
}
self.controller.users.update_user(
user_id, user_data=user_data, user_crafty_data=user_crafty_data
)
self.controller.management.add_to_audit_log(
exec_user["user_id"],
@ -1844,8 +1857,8 @@ class PanelHandler(BaseHandler):
name,
user_id,
superuser,
crafty_permissions_mask,
server_permissions_mask,
crafty_permissions_mask,
)
self.controller.management.add_to_audit_log(
@ -1874,13 +1887,15 @@ class PanelHandler(BaseHandler):
self.controller.management.add_to_audit_log(
exec_user["user_id"],
f"Generated a new API token for the key {key.name} "
f"from user with UID: {key.user.user_id}",
f"from user with UID: {key.user_id}",
server_id=0,
source_ip=self.get_remote_ip(),
)
self.write(
authentication.generate(key.user.user_id, {"token_id": key.token_id})
self.controller.authentication.generate(
key.user_id.user_id, {"token_id": key.token_id}
)
)
self.finish()
@ -1897,19 +1912,21 @@ class PanelHandler(BaseHandler):
password1 = bleach.clean(self.get_argument("password1", None))
email = bleach.clean(self.get_argument("email", "default@example.com"))
enabled = int(float(self.get_argument("enabled", "0")))
hints = True
lang = bleach.clean(
self.get_argument("lang", helper.get_setting("language"))
self.get_argument("lang", self.helper.get_setting("language"))
)
# We don't want a non-super user to be able to create a super user.
if superuser:
superuser = bleach.clean(self.get_argument("superuser", "0"))
new_superuser = bleach.clean(self.get_argument("superuser", "0"))
else:
superuser = "0"
new_superuser = "0"
if superuser == "1":
superuser = True
new_superuser = True
else:
superuser = False
new_superuser = False
if Enum_Permissions_Crafty.User_Config not in exec_user_crafty_permissions:
if EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions:
self.redirect(
"/panel/error?error=Unauthorized access: not a user editor"
)
@ -1935,12 +1952,9 @@ class PanelHandler(BaseHandler):
password=password0,
email=email,
enabled=enabled,
superuser=superuser,
superuser=new_superuser,
)
user_data = {
"roles": roles,
"lang": lang,
}
user_data = {"roles": roles, "lang": lang, "hints": True}
user_crafty_data = {
"permissions_mask": permissions_mask,
"server_quantity": server_quantity,
@ -1967,7 +1981,7 @@ class PanelHandler(BaseHandler):
role_id = bleach.clean(self.get_argument("id", None))
role_name = bleach.clean(self.get_argument("role_name", None))
if Enum_Permissions_Crafty.Roles_Config not in exec_user_crafty_permissions:
if EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions:
self.redirect(
"/panel/error?error=Unauthorized access: not a role editor"
)
@ -2003,7 +2017,7 @@ class PanelHandler(BaseHandler):
elif page == "add_role":
role_name = bleach.clean(self.get_argument("role_name", None))
if Enum_Permissions_Crafty.Roles_Config not in exec_user_crafty_permissions:
if EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions:
self.redirect(
"/panel/error?error=Unauthorized access: not a role editor"
)
@ -2042,8 +2056,8 @@ class PanelHandler(BaseHandler):
else:
self.set_status(404)
page_data = {
"lang": helper.get_setting("language"),
"lang_page": helper.getLangPage(helper.get_setting("language")),
"lang": self.helper.get_setting("language"),
"lang_page": Helpers.get_lang_page(self.helper.get_setting("language")),
}
self.render(
"public/404.html", translate=self.translator.translate, data=page_data
@ -2059,12 +2073,12 @@ class PanelHandler(BaseHandler):
page_data = {
# todo: make this actually pull and compare version data
"update_available": False,
"version_data": helper.get_version_string(),
"version_data": self.helper.get_version_string(),
"user_data": exec_user,
"hosts_data": self.controller.management.get_latest_hosts_stats(),
"show_contribute": helper.get_setting("show_contribute_link", True),
"show_contribute": self.helper.get_setting("show_contribute_link", True),
"lang": self.controller.users.get_user_lang_by_id(exec_user["user_id"]),
"lang_page": helper.getLangPage(
"lang_page": Helpers.get_lang_page(
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
),
}

View File

@ -1,24 +1,17 @@
import logging
import bleach
from app.classes.models.users import Users
from app.classes.shared.authentication import authentication
from app.classes.shared.helpers import helper
from app.classes.shared.main_models import fn
from app.classes.shared.helpers import Helpers
from app.classes.models.users import HelperUsers
from app.classes.web.base_handler import BaseHandler
try:
import bleach
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
logger = logging.getLogger(__name__)
class PublicHandler(BaseHandler):
def set_current_user(self, user_id: str = None):
expire_days = helper.get_setting("cookie_expire")
expire_days = self.helper.get_setting("cookie_expire")
# if helper comes back with false
if not expire_days:
@ -26,7 +19,9 @@ class PublicHandler(BaseHandler):
if user_id is not None:
self.set_cookie(
"token", authentication.generate(user_id), expires_days=int(expire_days)
"token",
self.controller.authentication.generate(user_id),
expires_days=int(expire_days),
)
else:
self.clear_cookie("token")
@ -37,14 +32,18 @@ class PublicHandler(BaseHandler):
error = bleach.clean(self.get_argument("error", "Invalid Login!"))
error_msg = bleach.clean(self.get_argument("error_msg", ""))
page_data = {
"version": helper.get_version_string(),
"error": error,
"lang": helper.get_setting("language"),
"lang_page": helper.getLangPage(helper.get_setting("language")),
"query": "",
}
try:
page_data = {
"version": self.helper.get_version_string(),
"error": error,
"lang": self.helper.get_setting("language"),
"lang_page": self.helper.get_lang_page(
self.helper.get_setting("language")
),
"query": "",
}
except:
self.redirect("/public/login.html")
if self.request.query:
page_data["query"] = self.request.query
@ -88,10 +87,10 @@ class PublicHandler(BaseHandler):
error_msg = bleach.clean(self.get_argument("error_msg", ""))
page_data = {
"version": helper.get_version_string(),
"version": self.helper.get_version_string(),
"error": error,
"lang": helper.get_setting("language"),
"lang_page": helper.getLangPage(helper.get_setting("language")),
"lang": self.helper.get_setting("language"),
"lang_page": self.helper.get_lang_page(self.helper.get_setting("language")),
"query": "",
}
if self.request.query:
@ -107,9 +106,21 @@ class PublicHandler(BaseHandler):
entered_password = bleach.clean(self.get_argument("password"))
# pylint: disable=no-member
user_data = Users.get_or_none(
fn.Lower(Users.username) == entered_username.lower()
)
try:
user_id = HelperUsers.get_user_id_by_name(entered_username.lower())
user_data = HelperUsers.get_user_model(user_id)
except:
error_msg = "Incorrect username or password. Please try again."
# self.clear_cookie("user")
# self.clear_cookie("user_data")
self.clear_cookie("token")
if self.request.query:
self.redirect(
f"/public/login?error_msg={error_msg}&{self.request.query}"
)
else:
self.redirect(f"/public/login?error_msg={error_msg}")
return
# if we don't have a user
if not user_data:
@ -142,7 +153,7 @@ class PublicHandler(BaseHandler):
self.redirect(f"/public/login?error_msg={error_msg}")
return
login_result = helper.verify_pass(entered_password, user_data.password)
login_result = self.helper.verify_pass(entered_password, user_data.password)
# Valid Login
if login_result:
@ -152,14 +163,9 @@ class PublicHandler(BaseHandler):
)
# record this login
q = (
Users.select()
.where(Users.username == entered_username.lower())
.get()
)
q.last_ip = self.get_remote_ip()
q.last_login = helper.get_time_as_string()
q.save()
user_data.last_ip = self.get_remote_ip()
user_data.last_login = Helpers.get_time_as_string()
user_data.save()
# log this login
self.controller.management.add_to_audit_log(

View File

@ -1,31 +1,28 @@
import json
import logging
import os
import tornado.web
import tornado.escape
import bleach
import libgravatar
import requests
from app.classes.minecraft.serverjars import server_jar_obj
from app.classes.models.crafty_permissions import Enum_Permissions_Crafty
from app.classes.shared.helpers import helper
from app.classes.shared.file_helpers import file_helper
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
from app.classes.shared.helpers import Helpers
from app.classes.shared.file_helpers import FileHelpers
from app.classes.web.base_handler import BaseHandler
try:
import tornado.web
import tornado.escape
import bleach
import libgravatar
import requests
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
logger = logging.getLogger(__name__)
class ServerHandler(BaseHandler):
@tornado.web.authenticated
def get(self, page):
# pylint: disable=unused-variable
api_key, token_data, exec_user = self.current_user
(
api_key,
_token_data,
exec_user,
) = self.current_user
superuser = exec_user["superuser"]
if api_key is not None:
superuser = superuser and api_key.superuser
@ -58,15 +55,15 @@ class ServerHandler(BaseHandler):
template = "public/404.html"
page_data = {
"version_data": helper.get_version_string(),
"version_data": self.helper.get_version_string(),
"user_data": exec_user,
"user_role": exec_user_role,
"roles": list_roles,
"user_crafty_permissions": exec_user_crafty_permissions,
"crafty_permissions": {
"Server_Creation": Enum_Permissions_Crafty.Server_Creation,
"User_Config": Enum_Permissions_Crafty.User_Config,
"Roles_Config": Enum_Permissions_Crafty.Roles_Config,
"Server_Creation": EnumPermissionsCrafty.SERVER_CREATION,
"User_Config": EnumPermissionsCrafty.USER_CONFIG,
"Roles_Config": EnumPermissionsCrafty.ROLES_CONFIG,
},
"server_stats": {
"total": len(self.controller.list_defined_servers()),
@ -78,9 +75,9 @@ class ServerHandler(BaseHandler):
},
"hosts_data": self.controller.management.get_latest_hosts_stats(),
"menu_servers": defined_servers,
"show_contribute": helper.get_setting("show_contribute_link", True),
"show_contribute": self.helper.get_setting("show_contribute_link", True),
"lang": self.controller.users.get_user_lang_by_id(exec_user["user_id"]),
"lang_page": helper.getLangPage(
"lang_page": Helpers.get_lang_page(
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
),
"api_key": {
@ -95,14 +92,16 @@ class ServerHandler(BaseHandler):
"superuser": superuser,
}
if helper.get_setting("allow_nsfw_profile_pictures"):
if self.helper.get_setting("allow_nsfw_profile_pictures"):
rating = "x"
else:
rating = "g"
if exec_user["email"] != "default@example.com" or "":
g = libgravatar.Gravatar(libgravatar.sanitize_email(exec_user["email"]))
url = g.get_image(
gravatar = libgravatar.Gravatar(
libgravatar.sanitize_email(exec_user["email"])
)
url = gravatar.get_image(
size=80,
default="404",
force_default=False,
@ -110,9 +109,12 @@ class ServerHandler(BaseHandler):
filetype_extension=False,
use_ssl=True,
) # + "?d=404"
if requests.head(url).status_code != 404:
profile_url = url
else:
try:
if requests.head(url).status_code != 404:
profile_url = url
else:
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
except:
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
else:
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
@ -131,9 +133,10 @@ class ServerHandler(BaseHandler):
)
return
page_data["server_types"] = server_jar_obj.get_serverjar_data()
page_data["online"] = Helpers.check_internet()
page_data["server_types"] = self.controller.server_jars.get_serverjar_data()
page_data["js_server_types"] = json.dumps(
server_jar_obj.get_serverjar_data()
self.controller.server_jars.get_serverjar_data()
)
template = "server/wizard.html"
@ -157,8 +160,7 @@ class ServerHandler(BaseHandler):
@tornado.web.authenticated
def post(self, page):
# pylint: disable=unused-variable
api_key, token_data, exec_user = self.current_user
api_key, _token_data, exec_user = self.current_user
superuser = exec_user["superuser"]
if api_key is not None:
superuser = superuser and api_key.superuser
@ -167,9 +169,9 @@ class ServerHandler(BaseHandler):
page_data = {
"version_data": "version_data_here", # TODO
"user_data": exec_user,
"show_contribute": helper.get_setting("show_contribute_link", True),
"show_contribute": self.helper.get_setting("show_contribute_link", True),
"lang": self.controller.users.get_user_lang_by_id(exec_user["user_id"]),
"lang_page": helper.getLangPage(
"lang_page": Helpers.get_lang_page(
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
),
}
@ -200,15 +202,17 @@ class ServerHandler(BaseHandler):
server_data.get("server_name") + f" (Copy {name_counter})"
)
new_server_uuid = helper.create_uuid()
new_server_uuid = Helpers.create_uuid()
while os.path.exists(
os.path.join(helper.servers_dir, new_server_uuid)
os.path.join(self.helper.servers_dir, new_server_uuid)
):
new_server_uuid = helper.create_uuid()
new_server_path = os.path.join(helper.servers_dir, new_server_uuid)
new_server_uuid = Helpers.create_uuid()
new_server_path = os.path.join(
self.helper.servers_dir, new_server_uuid
)
# copy the old server
file_helper.copy_dir(server_data.get("path"), new_server_path)
FileHelpers.copy_dir(server_data.get("path"), new_server_path)
# TODO get old server DB data to individual variables
stop_command = server_data.get("stop_command")
@ -217,8 +221,9 @@ class ServerHandler(BaseHandler):
).replace(server_uuid, new_server_uuid)
new_executable = server_data.get("executable")
new_server_log_file = str(
helper.get_os_understandable_path(server_data.get("log_path"))
Helpers.get_os_understandable_path(server_data.get("log_path"))
).replace(server_uuid, new_server_uuid)
backup_path = os.path.join(self.helper.backup_path, new_server_uuid)
server_port = server_data.get("server_port")
server_type = server_data.get("type")
@ -226,7 +231,7 @@ class ServerHandler(BaseHandler):
new_server_name,
new_server_uuid,
new_server_path,
"",
backup_path,
new_server_command,
new_executable,
new_server_log_file,
@ -295,7 +300,7 @@ class ServerHandler(BaseHandler):
elif import_type == "import_zip":
# here import_server_path means the zip path
zip_path = bleach.clean(self.get_argument("root_path"))
good_path = helper.check_path_exists(zip_path)
good_path = Helpers.check_path_exists(zip_path)
if not good_path:
self.redirect("/panel/error?error=Temp path not found!")
return
@ -318,7 +323,7 @@ class ServerHandler(BaseHandler):
self.get_remote_ip(),
)
# deletes temp dir
file_helper.del_dirs(zip_path)
FileHelpers.del_dirs(zip_path)
else:
if len(server_parts) != 2:
self.redirect("/panel/error?error=Invalid server data")
@ -326,7 +331,6 @@ class ServerHandler(BaseHandler):
server_type, server_version = server_parts
# TODO: add server type check here and call the correct server
# add functions if not a jar
role_ids = self.controller.users.get_user_roles_id(exec_user["user_id"])
new_server_id = self.controller.create_jar_server(
server_type, server_version, server_name, min_mem, max_mem, port
)
@ -413,7 +417,7 @@ class ServerHandler(BaseHandler):
elif import_type == "import_zip":
# here import_server_path means the zip path
zip_path = bleach.clean(self.get_argument("root_path"))
good_path = helper.check_path_exists(zip_path)
good_path = Helpers.check_path_exists(zip_path)
if not good_path:
self.redirect("/panel/error?error=Temp path not found!")
return
@ -436,7 +440,7 @@ class ServerHandler(BaseHandler):
self.get_remote_ip(),
)
# deletes temp dir
file_helper.del_dirs(zip_path)
FileHelpers.del_dirs(zip_path)
else:
if len(server_parts) != 2:
self.redirect("/panel/error?error=Invalid server data")
@ -444,7 +448,6 @@ class ServerHandler(BaseHandler):
server_type, server_version = server_parts
# TODO: add server type check here and call the correct server
# add functions if not a jar
role_ids = self.controller.users.get_user_roles_id(exec_user["user_id"])
new_server_id = self.controller.create_jar_server(
server_type, server_version, server_name, min_mem, max_mem, port
)

View File

@ -1,6 +1,5 @@
import logging
from app.classes.shared.helpers import helper
from app.classes.web.base_handler import BaseHandler
logger = logging.getLogger(__name__)
@ -9,8 +8,10 @@ logger = logging.getLogger(__name__)
class StatusHandler(BaseHandler):
def get(self):
page_data = {}
page_data["lang"] = helper.get_setting("language")
page_data["lang_page"] = helper.getLangPage(helper.get_setting("language"))
page_data["lang"] = self.helper.get_setting("language")
page_data["lang_page"] = self.helper.get_lang_page(
self.helper.get_setting("language")
)
page_data["servers"] = self.controller.servers.get_all_servers_stats()
running = 0
for srv in page_data["servers"]:

View File

@ -3,43 +3,50 @@ import sys
import json
import asyncio
import logging
import tornado.web
import tornado.ioloop
import tornado.log
import tornado.template
import tornado.escape
import tornado.locale
import tornado.httpserver
from app.classes.shared.translation import translation
from app.classes.shared.console import console
from app.classes.shared.helpers import helper
from app.classes.shared.console import Console
from app.classes.shared.helpers import Helpers
from app.classes.web.file_handler import FileHandler
from app.classes.web.public_handler import PublicHandler
from app.classes.web.panel_handler import PanelHandler
from app.classes.web.default_handler import DefaultHandler
from app.classes.web.server_handler import ServerHandler
from app.classes.web.ajax_handler import AjaxHandler
from app.classes.web.api_handler import ServersStats, NodeStats
from app.classes.web.api_handler import (
ServersStats,
NodeStats,
ServerBackup,
StartServer,
StopServer,
RestartServer,
CreateUser,
DeleteUser,
ListServers,
SendCommand,
)
from app.classes.web.websocket_handler import SocketHandler
from app.classes.web.static_handler import CustomStaticHandler
from app.classes.web.upload_handler import UploadHandler
from app.classes.web.http_handler import HTTPHandler, HTTPHandlerPage
from app.classes.web.status_handler import StatusHandler
try:
import tornado.web
import tornado.ioloop
import tornado.log
import tornado.template
import tornado.escape
import tornado.locale
import tornado.httpserver
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
logger = logging.getLogger(__name__)
class Webserver:
def __init__(self, controller, tasks_manager):
def __init__(self, helper, controller, tasks_manager):
self.ioloop = None
self.HTTP_Server = None
self.HTTPS_Server = None
self.http_server = None
self.https_server = None
self.helper = helper
self.controller = controller
self.tasks_manager = tasks_manager
self._asyncio_patch()
@ -92,16 +99,16 @@ class Webserver:
def run_tornado(self):
# let's verify we have an SSL cert
helper.create_self_signed_cert()
self.helper.create_self_signed_cert()
http_port = helper.get_setting("http_port")
https_port = helper.get_setting("https_port")
http_port = self.helper.get_setting("http_port")
https_port = self.helper.get_setting("https_port")
debug_errors = helper.get_setting("show_errors")
cookie_secret = helper.get_setting("cookie_secret")
debug_errors = self.helper.get_setting("show_errors")
cookie_secret = self.helper.get_setting("cookie_secret")
if cookie_secret is False:
cookie_secret = helper.random_string_generator(32)
cookie_secret = self.helper.random_string_generator(32)
if not http_port:
http_port = 8000
@ -111,10 +118,10 @@ class Webserver:
cert_objects = {
"certfile": os.path.join(
helper.config_dir, "web", "certs", "commander.cert.pem"
self.helper.config_dir, "web", "certs", "commander.cert.pem"
),
"keyfile": os.path.join(
helper.config_dir, "web", "certs", "commander.key.pem"
self.helper.config_dir, "web", "certs", "commander.key.pem"
),
}
@ -128,9 +135,10 @@ class Webserver:
tornado.locale.set_default_locale("en_EN")
handler_args = {
"helper": self.helper,
"controller": self.controller,
"tasks_manager": self.tasks_manager,
"translator": translation,
"translator": self.helper.translation,
}
handlers = [
(r"/", DefaultHandler, handler_args),
@ -139,17 +147,26 @@ class Webserver:
(r"/server/(.*)", ServerHandler, handler_args),
(r"/ajax/(.*)", AjaxHandler, handler_args),
(r"/files/(.*)", FileHandler, handler_args),
(r"/api/stats/servers", ServersStats, handler_args),
(r"/api/stats/node", NodeStats, handler_args),
(r"/ws", SocketHandler, handler_args),
(r"/upload", UploadHandler, handler_args),
(r"/status", StatusHandler, handler_args),
# API Routes
(r"/api/v1/stats/servers", ServersStats, handler_args),
(r"/api/v1/stats/node", NodeStats, handler_args),
(r"/api/v1/server/send_command", SendCommand, handler_args),
(r"/api/v1/server/backup", ServerBackup, handler_args),
(r"/api/v1/server/start", StartServer, handler_args),
(r"/api/v1/server/stop", StopServer, handler_args),
(r"/api/v1/server/restart", RestartServer, handler_args),
(r"/api/v1/list_servers", ListServers, handler_args),
(r"/api/v1/users/create_user", CreateUser, handler_args),
(r"/api/v1/users/delete_user", DeleteUser, handler_args),
]
app = tornado.web.Application(
handlers,
template_path=os.path.join(helper.webroot, "templates"),
static_path=os.path.join(helper.webroot, "static"),
template_path=os.path.join(self.helper.webroot, "templates"),
static_path=os.path.join(self.helper.webroot, "static"),
debug=debug_errors,
cookie_secret=cookie_secret,
xsrf_cookies=True,
@ -160,7 +177,7 @@ class Webserver:
static_handler_class=CustomStaticHandler,
serve_traceback=debug_errors,
)
HTTPhanders = [
http_handers = [
(r"/", HTTPHandler, handler_args),
(r"/public/(.*)", HTTPHandlerPage, handler_args),
(r"/panel/(.*)", HTTPHandlerPage, handler_args),
@ -171,10 +188,10 @@ class Webserver:
(r"/ws", HTTPHandlerPage, handler_args),
(r"/upload", HTTPHandlerPage, handler_args),
]
HTTPapp = tornado.web.Application(
HTTPhanders,
template_path=os.path.join(helper.webroot, "templates"),
static_path=os.path.join(helper.webroot, "static"),
http_app = tornado.web.Application(
http_handers,
template_path=os.path.join(self.helper.webroot, "templates"),
static_path=os.path.join(self.helper.webroot, "static"),
debug=debug_errors,
cookie_secret=cookie_secret,
xsrf_cookies=True,
@ -185,31 +202,31 @@ class Webserver:
serve_traceback=debug_errors,
)
self.HTTP_Server = tornado.httpserver.HTTPServer(HTTPapp)
self.HTTP_Server.listen(http_port)
self.http_server = tornado.httpserver.HTTPServer(http_app)
self.http_server.listen(http_port)
self.HTTPS_Server = tornado.httpserver.HTTPServer(app, ssl_options=cert_objects)
self.HTTPS_Server.listen(https_port)
self.https_server = tornado.httpserver.HTTPServer(app, ssl_options=cert_objects)
self.https_server.listen(https_port)
logger.info(
f"https://{helper.get_local_ip()}:{https_port} "
f"https://{Helpers.get_local_ip()}:{https_port} "
f"is up and ready for connections."
)
console.info(
f"https://{helper.get_local_ip()}:{https_port} "
Console.info(
f"https://{Helpers.get_local_ip()}:{https_port} "
f"is up and ready for connections."
)
console.info("Server Init Complete: Listening For Connections:")
Console.info("Server Init Complete: Listening For Connections:")
self.ioloop = tornado.ioloop.IOLoop.current()
self.ioloop.start()
def stop_web_server(self):
logger.info("Shutting Down Web Server")
console.info("Shutting Down Web Server")
Console.info("Shutting Down Web Server")
self.ioloop.stop()
self.HTTP_Server.stop()
self.HTTPS_Server.stop()
self.http_server.stop()
self.https_server.stop()
logger.info("Web Server Stopped")
console.info("Web Server Stopped")
Console.info("Web Server Stopped")

View File

@ -1,48 +1,66 @@
import logging
import os
import time
import tornado.web
import tornado.options
import tornado.httpserver
from app.classes.models.server_permissions import Enum_Permissions_Server
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
from app.classes.models.server_permissions import EnumPermissionsServer
from app.classes.shared.console import Console
from app.classes.shared.helpers import Helpers
from app.classes.shared.main_controller import Controller
from app.classes.web.websocket_helper import websocket_helper
from app.classes.web.base_handler import BaseHandler
try:
import tornado.web
import tornado.options
import tornado.httpserver
except ModuleNotFoundError as ex:
helper.auto_installer_fix(ex)
logger = logging.getLogger(__name__)
# Class & Function Defination
MAX_STREAMED_SIZE = 1024 * 1024 * 1024
@tornado.web.stream_request_body
class UploadHandler(BaseHandler):
# noinspection PyAttributeOutsideInit
def initialize(
self, controller: Controller = None, tasks_manager=None, translator=None
self,
helper: Helpers = None,
controller: Controller = None,
tasks_manager=None,
translator=None,
):
self.helper = helper
self.controller = controller
self.tasks_manager = tasks_manager
self.translator = translator
def prepare(self):
self.do_upload = True
# pylint: disable=unused-variable
api_key, token_data, exec_user = self.current_user
# Class & Function Defination
api_key, _token_data, exec_user = self.current_user
server_id = self.get_argument("server_id", None)
superuser = exec_user["superuser"]
if api_key is not None:
superuser = superuser and api_key.superuser
user_id = exec_user["user_id"]
stream_size_value = self.helper.get_setting("stream_size_GB")
max_streamed_size = (1024 * 1024 * 1024) * stream_size_value
self.content_len = int(self.request.headers.get("Content-Length"))
if self.content_len > max_streamed_size:
logger.error(
f"User with ID {user_id} attempted to upload a file that"
f" exceeded the max body size."
)
self.helper.websocket_helper.broadcast_user(
user_id,
"send_start_error",
{
"error": self.helper.translation.translate(
"error",
"fileTooLarge",
self.controller.users.get_user_lang_by_id(user_id),
),
},
)
return
self.do_upload = True
if superuser:
exec_user_server_permissions = (
@ -65,20 +83,20 @@ class UploadHandler(BaseHandler):
if user_id is None:
logger.warning("User ID not found in upload handler call")
console.warning("User ID not found in upload handler call")
Console.warning("User ID not found in upload handler call")
self.do_upload = False
if server_id is None:
logger.warning("Server ID not found in upload handler call")
console.warning("Server ID not found in upload handler call")
Console.warning("Server ID not found in upload handler call")
self.do_upload = False
if Enum_Permissions_Server.Files not in exec_user_server_permissions:
if EnumPermissionsServer.FILES not in exec_user_server_permissions:
logger.warning(
f"User {user_id} tried to upload a file to "
f"{server_id} without permissions!"
)
console.warning(
Console.warning(
f"User {user_id} tried to upload a file to "
f"{server_id} without permissions!"
)
@ -88,8 +106,8 @@ class UploadHandler(BaseHandler):
filename = self.request.headers.get("X-FileName", None)
full_path = os.path.join(path, filename)
if not helper.in_path(
helper.get_os_understandable_path(
if not Helpers.in_path(
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
full_path,
@ -97,7 +115,7 @@ class UploadHandler(BaseHandler):
print(
user_id,
server_id,
helper.get_os_understandable_path(
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
full_path,
@ -106,7 +124,7 @@ class UploadHandler(BaseHandler):
f"User {user_id} tried to upload a file to {server_id} "
f"but the path is not inside of the server!"
)
console.warning(
Console.warning(
f"User {user_id} tried to upload a file to {server_id} "
f"but the path is not inside of the server!"
)
@ -119,7 +137,7 @@ class UploadHandler(BaseHandler):
logger.error(f"Upload failed with error: {e}")
self.do_upload = False
# If max_body_size is not set, you cannot upload files > 100MB
self.request.connection.set_max_body_size(MAX_STREAMED_SIZE)
self.request.connection.set_max_body_size(max_streamed_size)
def post(self):
logger.info("Upload completed")
@ -128,13 +146,13 @@ class UploadHandler(BaseHandler):
if self.do_upload:
time.sleep(5)
if files_left == 0:
websocket_helper.broadcast("close_upload_box", "success")
self.helper.websocket_helper.broadcast("close_upload_box", "success")
self.finish("success") # Nope, I'm sending "success"
self.f.close()
else:
time.sleep(5)
if files_left == 0:
websocket_helper.broadcast("close_upload_box", "error")
self.helper.websocket_helper.broadcast("close_upload_box", "error")
self.finish("error")
def data_received(self, chunk):

View File

@ -2,16 +2,9 @@ import json
import logging
import asyncio
from urllib.parse import parse_qsl
import tornado.websocket
from app.classes.shared.authentication import authentication
from app.classes.shared.helpers import helper
from app.classes.web.websocket_helper import websocket_helper
try:
import tornado.websocket
except ModuleNotFoundError as e:
helper.auto_installer_fix(e)
from app.classes.shared.helpers import Helpers
logger = logging.getLogger(__name__)
@ -24,7 +17,10 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
translator = None
io_loop = None
def initialize(self, controller=None, tasks_manager=None, translator=None):
def initialize(
self, helper=None, controller=None, tasks_manager=None, translator=None
):
self.helper = helper
self.controller = controller
self.tasks_manager = tasks_manager
self.translator = translator
@ -39,11 +35,11 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
return remote_ip
def get_user_id(self):
_, _, user = authentication.check(self.get_cookie("token"))
_, _, user = self.controller.authentication.check(self.get_cookie("token"))
return user["user_id"]
def check_auth(self):
return authentication.check_bool(self.get_cookie("token"))
return self.controller.authentication.check_bool(self.get_cookie("token"))
# pylint: disable=arguments-differ
def open(self):
@ -51,7 +47,7 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
if self.check_auth():
self.handle()
else:
websocket_helper.send_message(
self.helper.websocket_helper.send_message(
self, "notification", "Not authenticated for WebSocket connection"
)
self.close()
@ -62,7 +58,7 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
"Someone tried to connect via WebSocket without proper authentication",
self.get_remote_ip(),
)
websocket_helper.broadcast(
self.helper.websocket_helper.broadcast(
"notification",
"Someone tried to connect via WebSocket without proper authentication",
)
@ -74,10 +70,10 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
self.page = self.get_query_argument("page")
self.page_query_params = dict(
parse_qsl(
helper.remove_prefix(self.get_query_argument("page_query_params"), "?")
Helpers.remove_prefix(self.get_query_argument("page_query_params"), "?")
)
)
websocket_helper.add_client(self)
self.helper.websocket_helper.add_client(self)
logger.debug("Opened WebSocket connection")
# pylint: disable=arguments-renamed
@ -89,7 +85,7 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
logger.debug(f"Event Type: {message['event']}, Data: {message['data']}")
def on_close(self):
websocket_helper.remove_client(self)
self.helper.websocket_helper.remove_client(self)
logger.debug("Closed WebSocket connection")
async def write_message_int(self, message):

View File

@ -1,13 +1,14 @@
import json
import logging
from app.classes.shared.console import console
from app.classes.shared.console import Console
logger = logging.getLogger(__name__)
class WebSocketHelper:
def __init__(self):
def __init__(self, helper):
self.helper = helper
self.clients = set()
def add_client(self, client):
@ -16,8 +17,9 @@ class WebSocketHelper:
def remove_client(self, client):
self.clients.remove(client)
# pylint: disable=no-self-use
def send_message(self, client, event_type: str, data):
def send_message(
self, client, event_type: str, data
): # pylint: disable=no-self-use
if client.check_auth():
message = str(json.dumps({"event": event_type, "data": data}))
client.write_message_helper(message)
@ -101,10 +103,7 @@ class WebSocketHelper:
)
def disconnect_all(self):
console.info("Disconnecting WebSocket clients")
Console.info("Disconnecting WebSocket clients")
for client in self.clients:
client.close()
console.info("Disconnected WebSocket clients")
websocket_helper = WebSocketHelper()
Console.info("Disconnected WebSocket clients")

View File

@ -1,20 +1,26 @@
{
"https": true,
"http_port": 8000,
"https_port": 8443,
"language": "en_EN",
"cookie_expire": 30,
"cookie_secret": "random",
"apikey_secret": "random",
"show_errors": true,
"history_max_age": 7,
"stats_update_frequency": 30,
"delete_default_json": false,
"show_contribute_link": true,
"virtual_terminal_lines": 70,
"max_log_lines": 700,
"max_audit_entries": 300,
"disabled_language_files": ["lol_EN.json", ""],
"keywords": ["help", "chunk"],
"allow_nsfw_profile_pictures": false
}
"http_port": 8000,
"https_port": 8443,
"language": "en_EN",
"cookie_expire": 30,
"cookie_secret": "random",
"apikey_secret": "random",
"show_errors": true,
"history_max_age": 7,
"stats_update_frequency": 30,
"delete_default_json": false,
"show_contribute_link": true,
"virtual_terminal_lines": 70,
"max_log_lines": 700,
"max_audit_entries": 300,
"disabled_language_files": [
"lol_EN.json",
""
],
"stream_size_GB": 1,
"keywords": [
"help",
"chunk"
],
"allow_nsfw_profile_pictures": false
}

View File

@ -1,6 +1,6 @@
{
"major": 4,
"minor": 0,
"sub": 0,
"meta": "alpha.3.5"
}
"sub": 0,
"meta": "beta"
}

View File

@ -9,7 +9,7 @@
<div class="content-wrapper">
<!-- Page Title Header Starts-->
<!-- Page Title Header Starts-->
<div class="row page-title-header">
<div class="col-12">
<div class="page-header">
@ -24,35 +24,39 @@
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-history"></i> &nbsp;Audit Logs</h4>
<span class="too_small" title="{{ translate('dashboard', 'cannotSeeOnMobile', data['lang']) }}", data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}", data-placement="top"></span>
{% if data['user_data']['hints'] %}
<span class="too_small" title="{{ translate('dashboard', 'cannotSeeOnMobile', data['lang']) }}" ,
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
data-placement="top"></span>
{% end %}
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover" id="audit_table" style="overflow: scroll;" width="100%">
<thead>
<tr class="rounded">
<td>Username</td>
<td>Time</td>
<td>Action</td>
<td>Server ID</td>
<td>IP</td>
</tr>
<tr class="rounded">
<td>Username</td>
<td>Time</td>
<td>Action</td>
<td>Server ID</td>
<td>IP</td>
</tr>
</thead>
<tbody>
{% for row in data['audit_logs'] %}
<tr>
{% for row in data['audit_logs'] %}
<tr>
<td>{{ row['user_name'] }}</td>
<td>
{{ row['created'].strftime('%Y-%m-%d %H:%M:%S') }}
{{ row['created'].strftime('%Y-%m-%d %H:%M:%S') }}
</td>
<td>{{ row['log_msg'] }}</td>
<td>{{ row['server_id'] }}</td>
<td>{{ row['source_ip'] }}</td>
</tr>
{% end %}
</tr>
{% end %}
</tbody>
</table>
</table>
</div>
</div>
@ -60,9 +64,10 @@
</div>
</div>
<style>
.popover-body{
color: white !important;;
}
.popover-body {
color: white !important;
;
}
</style>
@ -76,37 +81,37 @@
{% block js %}
<script>
$( document ).ready(function() {
$(document).ready(function () {
console.log('ready for JS!')
$('#audit_table').DataTable({
'order': [1, 'desc']
}
);
});
});
</script>
<script>
$(document).ready(function(){
$(document).ready(function () {
$('[data-toggle="popover"]').popover();
if($(window).width() < 1000){
if ($(window).width() < 1000) {
$('.too_small').popover("show");
}
});
$(window).ready(function(){
$('body').click(function(){
$('.too_small').popover("hide");
});
$(window).ready(function () {
$('body').click(function () {
$('.too_small').popover("hide");
});
});
$(window).resize(function() {
$(window).resize(function () {
// This will execute whenever the window is resized
if($(window).width() < 1000){
if ($(window).width() < 1000) {
$('.too_small').popover("show");
}
else{
else {
$('.too_small').popover("hide");
} // New width
});
</script>
</script>
{% end %}

View File

@ -75,7 +75,8 @@
<h3 class="mb-0 font-weight-semibold" id="total_players">{{ data['num_players'] }}</h3>
</div>
<div class="wrapper my-auto ml-auto ml-lg-4">
<p class="mb-0 text-warning"><span id="max_players">0</span> {{ translate('dashboard', 'max', data['lang']) }}</p>
<p class="mb-0 text-warning"><span id="max_players">0</span> {{ translate('dashboard', 'max',
data['lang']) }}</p>
</div>
</div>
</div>
@ -92,10 +93,12 @@
<h4 class="card-title"><i class="fas fa-server"></i> &nbsp;{{ translate('dashboard', 'allServers',
data['lang']) }}</h4>
{% if len(data['servers']) > 0 %}
{% if data['user_data']['hints'] %}
<span class="too_small" title="{{ translate('dashboard', 'cannotSeeOnMobile', data['lang']) }}" ,
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
data-placement="top"></span>
{% end %}
{% end %}
<div><a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> &nbsp; {{
translate('dashboard', 'newServer', data['lang']) }}</a></div>
</div>
@ -230,7 +233,9 @@
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 />
<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' %}
@ -251,7 +256,8 @@
data['lang']) }}</span>
{% end %}
</td>
<span class="server-player-totals" id="server_players_{{server['server_data']['server_id']}}" data-players="{{ server['stats']['online']}}" data-max="{{ server['stats']['max'] }}"></span>
<span class="server-player-totals" id="server_players_{{server['server_data']['server_id']}}"
data-players="{{ server['stats']['online']}}" data-max="{{ server['stats']['max'] }}"></span>
</tr>
{% end %}
</tbody>
@ -754,11 +760,15 @@
$(".clone_button").click(function () {
server_id = $(this).attr("data-id");
send_command(server_id, 'clone_server');
bootbox.alert({
bootbox.dialog({
backdrop: true,
title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}',
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; {% raw translate("dashboard", "bePatientClone", data["lang"]) %} </div>'
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; {% raw translate("dashboard", "bePatientClone", data["lang"]) %} </div>',
closeButton: false,
});
setTimeout(function () {
location.reload();
}, 5000)
});
});
@ -798,7 +808,7 @@
$(document).on("mousedown keyup click", function (event) {
const value = $('.dataTables_filter input').val();
const is_mobile = $('#mobile').css('display') == 'none';
const is_mobile = $('#mobile').css('display') === 'none';
if ($("table#servers_table tbody").sortable("toArray").length > 1 && value === '' && !is_mobile) {
$("table#servers_table tbody").sortable("enable");
@ -857,7 +867,7 @@
// Fixes the appearance of a scrollbar when a user tries to drag an item too low
// Disabled on mobile since sorting is disabled on mobile
if ($('#mobile').css('display') != 'none') {
if ($('#mobile').css('display') !== 'none') {
$("div#servers_table_wrapper").css({ overflow: "hidden" });
}
});

View File

@ -9,11 +9,11 @@
<div class="content-wrapper">
<!-- Page Title Header Starts-->
<!-- Page Title Header Starts-->
<div class="row page-title-header">
<div class="col-12">
<div class="page-header">
<!-- TODO: Translate the following -->
<!-- TODO: Translate the following -->
<h4 class="page-title">{{ translate('panelConfig', 'pageTitle', data['lang']) }}</h4>
</div>
</div>
@ -30,11 +30,16 @@
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('panelConfig', 'users', data['lang']) }}</h4>
<span class="too_small" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}", data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}", data-placement="top"></span>
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('panelConfig', 'users', data['lang'])
}}</h4>
{% if data['user_data']['hints'] %}
<span class="too_small" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}" ,
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
data-placement="top"></span>
{% end %}
<!-- TODO: Translate the following -->
<div><a class="nav-link" href="/panel/add_user"><i class="fas fa-plus-circle"></i> &nbsp; {{ translate('panelConfig', 'newUser', data['lang']) }}</a></div>
<div><a class="nav-link" href="/panel/add_user"><i class="fas fa-plus-circle"></i> &nbsp; {{
translate('panelConfig', 'newUser', data['lang']) }}</a></div>
</div>
<div class="card-body">
<div class="table-responsive">
@ -50,38 +55,38 @@
</tr>
</thead>
<tbody>
{% for user in data['users'] %}
{% for user in data['users'] %}
<tr>
<td><i class="fas fa-user"></i> {{ user.username }}</td>
<td>
{% if user.enabled %}
<span class="text-success">
<i class="fas fa-check-square"></i> Yes
</span>
<span class="text-success">
<i class="fas fa-check-square"></i> Yes
</span>
{% else %}
<span class="text-danger">
<i class="far fa-times-square"></i> No
</span>
<span class="text-danger">
<i class="far fa-times-square"></i> No
</span>
{% end %}
</td>
<td id="server_list_{{user.user_id}}">
<ul id="{{user.user_id}}">
<ul id="{{user.user_id}}">
{% for item in data['auth-servers'][user.user_id] %}
<li>{{item}}</li>
<li>{{item}}</li>
{% end %}
</ul>
</td>
<td id="role_list_{{user.user_id}}">
<ul>
{% for item in data['user-roles'][user.user_id] %}
{% for item in data['user-roles'][user.user_id] %}
<li data-toggle="tooltip" title="{{ item }}">{{item}}</li>
{% end %}
{% end %}
</ul>
</td>
<td><a href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-pencil-alt"></i></a></td>
</tr>
{% end %}
{% end %}
</tbody>
</table>
</div>
@ -93,9 +98,15 @@
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'roles', data['lang']) }}</h4>
<span class="too_small2" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}", data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}", data-placement="top"></span>
<div><a class="nav-link" href="/panel/add_role"><i class="fas fa-plus-circle"></i> &nbsp; {{ translate('panelConfig', 'newRole', data['lang']) }}</a></div>
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'roles',
data['lang']) }}</h4>
{% if data['user_data']['hints'] %}
<span class="too_small2" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}" ,
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
data-placement="top"></span>
{% end %}
<div><a class="nav-link" href="/panel/add_role"><i class="fas fa-plus-circle"></i> &nbsp; {{
translate('panelConfig', 'newRole', data['lang']) }}</a></div>
</div>
<div class="card-body">
<div class="table-responsive">
@ -110,7 +121,7 @@
</tr>
</thead>
<tbody>
{% for role in data['roles'] %}
{% for role in data['roles'] %}
<tr>
<td>{{ role.role_name }}</td>
<td id="role_list_{{role.role_id}}">
@ -120,19 +131,20 @@
{% end %}
</ul>
</td>
<td><ul>
{% for user in data['users'] %}
<td>
<ul>
{% for user in data['users'] %}
{% for ruser in data['user-roles'][user.user_id] %}
{% if ruser == role.role_name %}
<li>{{ user.username }}</li>
{% end %}
{% end %}
{% end %}
</ul>
{% if ruser == role.role_name %}
<li>{{ user.username }}</li>
{% end %}
{% end %}
{% end %}
</ul>
</td>
<td><a href="/panel/edit_role?id={{role.role_id}}"><i class="fas fa-pencil-alt"></i></a></td>
</tr>
{% end %}
{% end %}
</tbody>
</table>
@ -146,10 +158,12 @@
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'adminControls', data['lang']) }}</h4>
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'adminControls',
data['lang']) }}</h4>
</div>
<div class="card-body">
<button type="button" class="btn btn-outline-danger clear-comm">{{ translate('panelConfig', 'clearComms', data['lang']) }}</button>
<button type="button" class="btn btn-outline-danger clear-comm">{{ translate('panelConfig',
'clearComms', data['lang']) }}</button>
</div>
</div>
@ -165,9 +179,10 @@
</div>
<style>
.popover-body{
color: white !important;;
}
.popover-body {
color: white !important;
;
}
</style>
<!-- content-wrapper ends -->
@ -175,46 +190,46 @@
{% block js %}
<script>
$(document).ready(function(){
$('[data-toggle="popover"]').popover();
if($(window).width() < 1000){
$('.too_small').popover("show");
$('.too_small2').popover("show");
}
$(document).ready(function () {
$('[data-toggle="popover"]').popover();
if ($(window).width() < 1000) {
$('.too_small').popover("show");
$('.too_small2').popover("show");
}
});
$(window).ready(function(){
$('body').click(function(){
$('.too_small').popover("hide");
$('.too_small2').popover("hide");
});
$(window).ready(function () {
$('body').click(function () {
$('.too_small').popover("hide");
$('.too_small2').popover("hide");
});
});
$(window).resize(function() {
// This will execute whenever the window is resized
if($(window).width() < 1000){
$('.too_small').popover("show");
}
else{
$('.too_small').popover("hide");
} // New width
if($(window).width() < 1000){
$('.too_small2').popover("show");
}
else{
$('.too_small2').popover("hide");
} // New width
});
});
$(window).resize(function () {
// This will execute whenever the window is resized
if ($(window).width() < 1000) {
$('.too_small').popover("show");
}
else {
$('.too_small').popover("hide");
} // New width
if ($(window).width() < 1000) {
$('.too_small2').popover("show");
}
else {
$('.too_small2').popover("hide");
} // New width
});
</script>
<script>
$( document ).ready(function() {
console.log('ready for JS!')
$(document).ready(function () {
console.log('ready for JS!')
});
});
$( ".show_button" ).click(function() {
$(".show_button").click(function () {
console.log("showing key");
api_key = $(this).attr("data-id");
api_key = $(this).attr("data-id");
bootbox.alert({
backdrop: true,
title: '',
@ -225,13 +240,13 @@ $( document ).ready(function() {
$('.clear-comm').click(function () {
var token = getCookie("_xsrf")
$.ajax({
type: "POST",
headers: {'X-XSRFToken': token},
url: '/ajax/clear_comm',
success: function (data) {
},
type: "POST",
headers: { 'X-XSRFToken': token },
url: '/ajax/clear_comm',
success: function (data) {
},
});
})
})
</script>
{% end %}

View File

@ -9,22 +9,22 @@
<div class="content-wrapper">
<!-- Page Title Header Starts-->
<!-- Page Title Header Starts-->
<div class="row page-title-header">
<div class="col-12">
<div class="page-header">
{% if data['new_user'] %}
<h4 class="page-title">
{{ translate('userConfig', 'pageTitleNew', data['lang']) }}
<br />
<small>UID: N/A</small>
</h4>
<h4 class="page-title">
{{ translate('userConfig', 'pageTitleNew', data['lang']) }}
<br />
<small>UID: N/A</small>
</h4>
{% else %}
<h4 class="page-title">
{{ translate('userConfig', 'pageTitle', data['lang']) }} - {{ data['user']['user_id'] }}
<br />
<small>UID: {{ data['user']['user_id'] }}</small>
</h4>
<h4 class="page-title">
{{ translate('userConfig', 'pageTitle', data['lang']) }}
<br />
<small>UID: {{ data['user']['user_id'] }}</small>
</h4>
{% end %}
</div>
</div>
@ -37,198 +37,250 @@
<div class="col-sm-12 grid-margin">
<div class="card">
<div class="card-body pt-0">
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item">
<a class="nav-link active" href="/panel/{{ 'add_user' if data['new_user'] else 'edit_user' }}?id={{ data['user']['user_id'] }}&subpage=config" role="tab" aria-selected="true">
<i class="fas fa-cogs"></i> {{ translate('userConfig', 'config', data['lang']) }} - {{ data['user']['user_id'] }}</a>
</li>
{% if not data['new_user'] %}
<li class="nav-item">
<a class="nav-link" href="/panel/edit_user_apikeys?id={{ data['user']['user_id'] }}" role="tab" aria-selected="false">
<i class="fas fa-key"></i>{{ translate('userConfig', 'apiKey', data['lang']) }} - {{ data['user']['user_id'] }}</a>
</li>
{% end %}
</ul>
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item">
<a class="nav-link active"
href="/panel/{{ 'add_user' if data['new_user'] else 'edit_user' }}?id={{ data['user']['user_id'] }}&subpage=config"
role="tab" aria-selected="true">
<i class="fas fa-cogs"></i> {{ translate('userConfig', 'config', data['lang']) }}</a>
</li>
{% if not data['new_user'] %}
<li class="nav-item">
<a class="nav-link" href="/panel/edit_user_apikeys?id={{ data['user']['user_id'] }}" role="tab"
aria-selected="false">
<i class="fas fa-key"></i>{{ translate('userConfig', 'apiKey', data['lang']) }}</a>
</li>
{% end %}
</ul>
<div class="row">
<div class="col-md-6 col-sm-12">
{% if data['new_user'] %}
<form id="user_form" class="forms-sample" method="post" action="/panel/add_user">
{% else %}
<form id="user_form" class="forms-sample" method="post" action="/panel/edit_user">
<div class="row">
<div class="col-md-6 col-sm-12">
{% if data['new_user'] %}
<form id="user_form" class="forms-sample" method="post" action="/panel/add_user">
{% else %}
<form id="user_form" class="forms-sample" method="post" action="/panel/edit_user">
{% end %}
{% raw xsrf_form_html() %}
<input type="hidden" name="id" value="{{ data['user']['user_id'] }}">
<input type="hidden" name="subpage" value="config">
{% raw xsrf_form_html() %}
<input type="hidden" name="id" value="{{ data['user']['user_id'] }}">
<input type="hidden" name="subpage" value="config">
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user"></i> {{ translate('userConfig', 'userSettings', data['lang']) }} - {{ data['user']['user_id'] }}</h4>
</div>
<div class="card-body">
<div class="form-group">
<label class="form-label" for="username">{{ translate('userConfig', 'userName', data['lang']) }} - {{ data['user']['user_id'] }}<small class="text-muted ml-1"> - {{ translate('userConfig', 'userNameDesc', data['lang']) }} - {{ data['user']['user_id'] }}</small> </label>
<input type="text" class="form-control" name="username" id="username" value="{{ data['user']['username'] }}" placeholder="User Name" >
</div>
<div class="form-group">
<label class="form-label" for="password0">{{ translate('userConfig', 'password', data['lang']) }} - {{ data['user']['user_id'] }}<small class="text-muted ml-1"> - {{ translate('userConfig', 'leaveBlank', data['lang']) }} - {{ data['user']['user_id'] }}</small> </label>
<input type="password" class="form-control" name="password0" id="password0" value="" placeholder="Password" >
</div>
<div class="form-group">
<label class="form-label" for="password1">{{ translate('userConfig', 'repeat', data['lang']) }} - {{ data['user']['user_id'] }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'leaveBlank', data['lang']) }} - {{ data['user']['user_id'] }}</small> </label>
<input type="password" class="form-control" name="password1" id="password1" value="" placeholder="Repeat Password" >
</div>
<div class="form-group">
<label class="form-label" for="email">{{ translate('userConfig', 'gravEmail', data['lang']) }} - {{ data['user']['user_id'] }}<small class="text-muted ml-1"> - {{ translate('userConfig', 'gravDesc', data['lang']) }} - {{ data['user']['user_id'] }}</small> </label>
<input type="email" class="form-control" name="email" id="email" value="{{ data['user']['email'] }}" placeholder="Gravatar Email" >
</div>
<div class="form-group">
<label class="form-label" for="language">{{ translate('userConfig', 'userLang', data['lang']) }}</label>
<select class="form-select form-control form-control-lg select-css" id="language" name="language" form="user_form">
{% for lang in data['languages'] %}
{% if not 'incomplete' in lang %}
<option value="{{lang}}">{{lang}}</option>
{% else %}
<option value="{{lang}}" disabled>{{lang}}</option>
{% end %}
{% end %}
</select>
</div>
</div>
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user"></i> {{ translate('userConfig', 'userSettings',
data['lang']) }}</h4>
</div>
<div class="card-body">
<div class="form-group">
<label class="form-label" for="username">{{ translate('userConfig', 'userName', data['lang'])
}}<small class="text-muted ml-1"> - {{ translate('userConfig', 'userNameDesc', data['lang'])
}}</small> </label>
<input type="text" class="form-control" name="username" id="username"
value="{{ data['user']['username'] }}" placeholder="User Name">
</div>
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('userConfig', 'userRoles', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'userRolesDesc', data['lang']) }}</small></h4>
</div>
<div class="card-body">
<div class="form-group">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr class="rounded">
<th>{{ translate('userConfig', 'roleName', data['lang']) }}</th>
<th>{{ translate('userConfig', 'member', data['lang']) }}</th>
</tr>
</thead>
<tbody>
{% for role in data['roles_all'] %}
<tr>
<td>{{ role.role_name }}</td>
<td>
{% if role.role_id in data['user']['roles'] %}
<input type="checkbox" class="form-check-input" id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership" checked="" value="1">
{% else %}
<input type="checkbox" class="form-check-input" id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership" value="1">
{% end %}
</td>
</tr>
{% end %}
</tbody>
</table>
</div>
</div>
</div>
<div class="form-group">
<label class="form-label" for="password0">{{ translate('userConfig', 'password', data['lang'])
}}<small class="text-muted ml-1"> - {{ translate('userConfig', 'leaveBlank', data['lang']) }}
</small> </label>
<input type="password" class="form-control" name="password0" id="password0" value=""
placeholder="Password">
</div>
<!-- Put Permissions Crafty part here -->
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user-lock"></i> {{ translate('userConfig', 'craftyPerms', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'craftyPermDesc', data['lang']) }}</small></h4>
</div>
<div class="card-body">
<div class="form-group">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr class="rounded">
<th>{{ translate('userConfig', 'permName', data['lang']) }}</th>
<th>{{ translate('userConfig', 'auth', data['lang']) }}</th>
<th>{{ translate('userConfig', 'uses', data['lang']) }}</th>
</tr>
</thead>
<tbody>
{% for permission in data['permissions_all'] %}
<tr>
<td>{{ permission.name }}</td>
<td>
{% if permission in data['permissions_list'] %}
<input type="checkbox" class="form-check-input" id="permission_{{ permission.name }}" name="permission_{{ permission.name }}" checked="" value="1">
{% else %}
<input type="checkbox" class="form-check-input" id="permission_{{ permission.name }}" name="permission_{{ permission.name }}" value="1">
{% end %}
</td>
<td><input type="text" class="form-control" name="quantity_{{ permission.name }}" id="quantity_{{ permission.name }}" value="{{ data['quantity_server'][permission.name] }}"></td>
</tr>
{% end %}
</tbody>
</table>
</div>
</div>
</div>
<div class="form-group">
<label class="form-label" for="password1">{{ translate('userConfig', 'repeat', data['lang']) }}
<small class="text-muted ml-1"> - {{ translate('userConfig', 'leaveBlank', data['lang'])
}}</small> </label>
<input type="password" class="form-control" name="password1" id="password1" value=""
placeholder="Repeat Password">
</div>
<div class="form-check-flat">
<label for="enabled" class="form-check-label ml-4 mb-4">
{% if data['user']['enabled'] %}
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" checked="" value="1">{{ translate('userConfig', 'enabled', data['lang']) }}
<div class="form-group">
<label class="form-label" for="email">{{ translate('userConfig', 'gravEmail', data['lang'])
}}<small class="text-muted ml-1"> - {{ translate('userConfig', 'gravDesc', data['lang'])
}}</small> </label>
<input type="email" class="form-control" name="email" id="email"
value="{{ data['user']['email'] }}" placeholder="Gravatar Email">
</div>
<div class="form-group">
<label class="form-label" for="language">{{ translate('userConfig', 'userLang', data['lang'])
}}</label>
<select class="form-select form-control form-control-lg select-css" id="language"
name="language" form="user_form">
{% for lang in data['languages'] %}
{% if not 'incomplete' in lang %}
<option value="{{lang}}">{{lang}}</option>
{% else %}
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" value="1">{{ translate('userConfig', 'enabled', data['lang']) }}
<option value="{{lang}}" disabled>{{lang}}</option>
{% end %}
</label>
<label for="superuser" class="form-check-label ml-4 mb-4">
{% if data['user']['superuser'] %}
<input type="checkbox" onclick="superConfirm()" class="form-check-input" id="superuser" name="superuser" checked="" value="1" {{ data['super-disabled'] }} >{{ translate('userConfig', 'super', data['lang']) }}
{% else %}
<input type="checkbox" onclick="superConfirm()" class="form-check-input" id="superuser" name="superuser" {{ data['super-disabled'] }} value="1" >{{ translate('userConfig', 'super', data['lang']) }}
{% end %}
</label>
</div>
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{ translate('panelConfig', 'save', data['lang']) }}</button>
<button type="reset" onclick="location.href='/panel/panel_config'" class="btn btn-light"><i class="fas fa-undo-alt"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}</button>
</form>
</div>
<div class="col-md-6 col-sm-12">
<div class="card">
<div class="card-body">
<h4 class="card-title"><i class="fas fa-user-cog"></i> {{ translate('userConfig', 'configArea', data['lang']) }}</h4>
<p class="card-description"> {{ translate('userConfig', 'configAreaDesc', data['lang']) }}</p>
<blockquote class="blockquote">
<p class="mb-0">
{{ translate('userConfig', 'created', data['lang']) }} {{ str(data['user']['created']) }}
<br />
{{ translate('userConfig', 'lastLogin', data['lang']) }} {{ str(data['user']['last_login']) }}
<br />
{{ translate('userConfig', 'lastUpdate', data['lang']) }} {{ str(data['user']['last_update']) }}
<br />
{{ translate('userConfig', 'lastIP', data['lang']) }} {{ data['user']['last_ip'] }}
<br />
</p>
</blockquote>
</select>
</div>
</div>
<div class="text-center">
{% if data['new_user'] %}
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i>{{ translate('userConfig', 'deleteUserB', data['lang']) }}</a><br />
<small>{{ translate('userConfig', 'notExist', data['lang']) }}</small>
{% elif data['user']['superuser'] %}
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i> {{ translate('userConfig', 'deleteUserB', data['lang']) }}</a><br />
<small>{{ translate('userConfig', 'delSuper', data['lang']) }}</small>
{% else %}
<button class="btn btn-sm btn-danger delete-user"><i class="fas fa-trash"></i> {{ translate('userConfig', 'deleteUserB', data['lang']) }}</a>
{% end %}
</div>
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('userConfig', 'userRoles',
data['lang']) }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'userRolesDesc',
data['lang']) }}</small></h4>
</div>
</div>
<div class="card-body">
<div class="form-group">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr class="rounded">
<th>{{ translate('userConfig', 'roleName', data['lang']) }}</th>
<th>{{ translate('userConfig', 'member', data['lang']) }}</th>
</tr>
</thead>
<tbody>
{% for role in data['roles_all'] %}
<tr>
<td>{{ role.role_name }}</td>
<td>
{% if role.role_id in data['user']['roles'] %}
<input type="checkbox" class="form-check-input"
id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership"
checked="" value="1">
{% else %}
<input type="checkbox" class="form-check-input"
id="role_{{ role.role_id }}_membership" name="role_{{ role.role_id }}_membership"
value="1">
{% end %}
</td>
</tr>
{% end %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Put Permissions Crafty part here -->
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user-lock"></i> {{ translate('userConfig', 'craftyPerms',
data['lang']) }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'craftyPermDesc',
data['lang']) }}</small></h4>
</div>
<div class="card-body">
<div class="form-group">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr class="rounded">
<th>{{ translate('userConfig', 'permName', data['lang']) }}</th>
<th>{{ translate('userConfig', 'auth', data['lang']) }}</th>
<th>{{ translate('userConfig', 'uses', data['lang']) }}</th>
</tr>
</thead>
<tbody>
{% for permission in data['permissions_all'] %}
<tr>
<td>{{ permission.name }}</td>
<td>
{% if permission in data['permissions_list'] %}
<input type="checkbox" class="form-check-input" id="permission_{{ permission.name }}"
name="permission_{{ permission.name }}" checked="" value="1">
{% else %}
<input type="checkbox" class="form-check-input" id="permission_{{ permission.name }}"
name="permission_{{ permission.name }}" value="1">
{% end %}
</td>
<td><input type="text" class="form-control" name="quantity_{{ permission.name }}"
id="quantity_{{ permission.name }}"
value="{{ data['quantity_server'][permission.name] }}"></td>
</tr>
{% end %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="form-check-flat">
<label for="enabled" class="form-check-label ml-4 mb-4">
{% if data['user']['enabled'] %}
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" checked=""
value="1">{{ translate('userConfig', 'enabled', data['lang']) }}
{% else %}
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" value="1">{{
translate('userConfig', 'enabled', data['lang']) }}
{% end %}
</label>
<label for="superuser" class="form-check-label ml-4 mb-4">
{% if data['user']['superuser'] %}
<input type="checkbox" onclick="superConfirm()" class="form-check-input" id="superuser"
name="superuser" checked="" value="1" {{ data['super-disabled'] }}>{{ translate('userConfig',
'super', data['lang']) }}
{% else %}
<input type="checkbox" onclick="superConfirm()" class="form-check-input" id="superuser"
name="superuser" {{ data['super-disabled'] }} value="1">{{ translate('userConfig', 'super',
data['lang']) }}
{% end %}
</label>
<label for="hints" class="form-check-label ml-4 mb-4">
{% if data['user']['hints'] %}
<input type="checkbox" class="form-check-input" id="hints" name="hints" checked=""
value="1">Enable Hints?
{% else %}
<input type="checkbox" class="form-check-input" id="hints" name="hints" value="1"> Enable
Hints?
{% end %}
</label>
</div>
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{
translate('panelConfig', 'save', data['lang']) }}</button>
<button type="reset" onclick="location.href='/panel/panel_config'" class="btn btn-light"><i
class="fas fa-undo-alt"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}</button>
</form>
</div>
<div class="col-md-6 col-sm-12">
<div class="card">
<div class="card-body">
<h4 class="card-title"><i class="fas fa-user-cog"></i> {{ translate('userConfig', 'configArea',
data['lang']) }}</h4>
<p class="card-description"> {{ translate('userConfig', 'configAreaDesc', data['lang']) }}</p>
<blockquote class="blockquote">
<p class="mb-0">
{{ translate('userConfig', 'created', data['lang']) }} {{ str(data['user']['created']) }}
<br />
{{ translate('userConfig', 'lastLogin', data['lang']) }} {{ str(data['user']['last_login']) }}
<br />
{{ translate('userConfig', 'lastUpdate', data['lang']) }} {{ str(data['user']['last_update']) }}
<br />
{{ translate('userConfig', 'lastIP', data['lang']) }} {{ data['user']['last_ip'] }}
<br />
</p>
</blockquote>
</div>
</div>
<div class="text-center">
{% if data['new_user'] %}
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i>{{ translate('userConfig',
'deleteUserB', data['lang']) }}</a><br />
<small>{{ translate('userConfig', 'notExist', data['lang']) }}</small>
{% elif data['user']['superuser'] %}
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i> {{ translate('userConfig',
'deleteUserB', data['lang']) }}</a><br />
<small>{{ translate('userConfig', 'delSuper', data['lang']) }}</small>
{% else %}
<button class="btn btn-sm btn-danger delete-user"><i class="fas fa-trash"></i> {{
translate('userConfig', 'deleteUserB', data['lang']) }}</a>
{% end %}
</div>
</div>
</div>
</div>
</div>
</div>
@ -245,70 +297,70 @@
<script>
const userId = new URLSearchParams(document.location.search).get('id')
$( ".delete-user" ).click(function() {
$(".delete-user").click(function () {
var file_to_del = $(this).data("file");
console.log("User to delete is "+userId);
console.log("User to delete is " + userId);
bootbox.confirm({
title: "{% raw translate('userConfig', 'deleteUser', data['lang']) %} "+userId,
message: "{{ translate('userConfig', 'confirmDelete', data['lang']) }}",
buttons: {
cancel: {
label: '<i class="fas fa-times"></i> {{ translate("serverBackups", "cancel", data['lang']) }}'
title: "{% raw translate('userConfig', 'deleteUser', data['lang']) %} " + userId,
message: "{{ translate('userConfig', 'confirmDelete', data['lang']) }}",
buttons: {
cancel: {
label: '<i class="fas fa-times"></i> {{ translate("serverBackups", "cancel", data['lang']) }}'
},
confirm: {
className: 'btn-outline-danger',
label: '<i class="fas fa-check"></i> {{ translate("serverBackups", "confirm", data['lang']) }}'
}
},
callback: function (result) {
console.log(result);
if (result == true) {
location.href="/panel/remove_user?id="+userId;
confirm: {
className: 'btn-outline-danger',
label: '<i class="fas fa-check"></i> {{ translate("serverBackups", "confirm", data['lang']) }}'
}
},
callback: function (result) {
console.log(result);
if (result === true) {
location.href = "/panel/remove_user?id=" + userId;
}
}
});
});
});
function superConfirm() {
if (document.getElementById('superuser').checked){
bootbox.confirm({
title: "{{ translate('panelConfig', 'superConfirmTitle', data['lang']) }}",
message: "{{ translate('panelConfig', 'superConfirm', data['lang']) }}",
buttons: {
if (document.getElementById('superuser').checked) {
bootbox.confirm({
title: "{{ translate('panelConfig', 'superConfirmTitle', data['lang']) }}",
message: "{{ translate('panelConfig', 'superConfirm', data['lang']) }}",
buttons: {
cancel: {
label: '<i class="fa fa-times"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}'
label: '<i class="fa fa-times"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}'
},
confirm: {
className: 'btn-outline-warning',
label: '<i class="fa fa-check"></i> {{ translate('serverBackups', 'confirm', data['lang']) }}'
label: '<i class="fa fa-check"></i> {{ translate('serverBackups', 'confirm', data['lang']) }}'
}
},
callback: function (result) {
if (result == true){
},
callback: function (result) {
if (result === true) {
return;
}else{
} else {
document.getElementById('superuser').checked = false;
}
}
});
}else{
return
}
}
});
} else {
return
}
}
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
}
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
}
$( document ).ready(function() {
console.log( "ready!" );
$(document).ready(function () {
console.log("ready!");
});
});
</script>

View File

@ -5,39 +5,60 @@
<div class="row">
<div class="col-sm-4 mr-2">
{% if data['server_stats']['running'] %}
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span id="status" class="text-success">{{ translate('serverStats', 'online', data['lang']) }}</span><br />
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span id="started">{{ data['server_stats']['started'] }}</span><br />
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime">{{ translate('serverStats', 'errorCalculatingUptime', data['lang']) }}</span>
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span id="status"
class="text-success">{{ translate('serverStats', 'online', data['lang']) }}</span><br />
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span id="started">{{
data['server_stats']['started'] }}</span><br />
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime">{{
translate('serverStats', 'errorCalculatingUptime', data['lang']) }}</span>
{% elif data['server_stats']['crashed'] %}
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span id="status" class="text-danger"> <i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}</span><br />
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span id="started" class="text-danger"> <i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}</span><br />
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime" class="text-danger"> <i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}</span>
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span id="status" class="text-danger">
<i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang'])
}}</span><br />
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span id="started"
class="text-danger"> <i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed',
data['lang']) }}</span><br />
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime" class="text-danger">
<i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}</span>
{% else %}
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span id="status" class="text-warning">{{ translate('serverStats', 'offline', data['lang']) }}</span><br />
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span id="started" class="text-warning">{{ translate('serverStats', 'offline', data['lang']) }}</span><br />
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime" class="text-warning">{{ translate('serverStats', 'offline', data['lang']) }}</span>
<b>{{ translate('serverStats', 'serverStatus', data['lang']) }}:</b> <span id="status"
class="text-warning">{{ translate('serverStats', 'offline', data['lang']) }}</span><br />
<b>{{ translate('serverStats', 'serverStarted', data['lang']) }}:</b> <span id="started"
class="text-warning">{{ translate('serverStats', 'offline', data['lang']) }}</span><br />
<b>{{ translate('serverStats', 'serverUptime', data['lang']) }}:</b> <span id="uptime"
class="text-warning">{{ translate('serverStats', 'offline', data['lang']) }}</span>
{% end %}
<br>
<b>{{ translate('serverStats', 'serverTimeZone', data['lang']) }}:</b> <span class="text-info">{{ data['serverTZ'] }}</span>
<b>{{ translate('serverStats', 'serverTimeZone', data['lang']) }}:</b> <span class="text-info">{{
data['serverTZ'] }}</span>
</div>
<div class="col-sm-3 mr-2">
<b>{{ translate('serverStats', 'cpuUsage', data['lang']) }}:</b> <span id="cpu">{{ data['server_stats']['cpu'] }}%</span> <br />
<b>{{ translate('serverStats', 'memUsage', data['lang']) }}:</b> <span id="mem" >{{ data['server_stats']['mem'] }}</span> <br />
<b>{{ translate('serverStats', 'cpuUsage', data['lang']) }}:</b> <span id="cpu">{{
data['server_stats']['cpu'] }}%</span> <br />
<b>{{ translate('serverStats', 'memUsage', data['lang']) }}:</b> <span id="mem">{{
data['server_stats']['mem'] }}</span> <br />
{% if data['server_stats']['int_ping_results'] %}
<b>{{ translate('serverStats', 'players', data['lang']) }}:</b> <span id="players" >{{ data['server_stats']['online'] }} / {{ data['server_stats']['max'] }}</span><br />
<b>{{ translate('serverStats', 'players', data['lang']) }}:</b> <span id="players">{{
data['server_stats']['online'] }} / {{ data['server_stats']['max'] }}</span><br />
{% else %}
<b>{{ translate('serverStats', 'players', data['lang']) }}:</b> <span id="players" >0/0</span><br />
<b>{{ translate('serverStats', 'players', data['lang']) }}:</b> <span id="players">0/0</span><br />
{% end %}
</div>
<div class="col-sm-3 mr-2">
{% if data['server_stats']['version'] != 'False' %}
<b>{{ translate('serverStats', 'version', data['lang']) }}:</b> <span id="version">{{ data['server_stats']['version'] }}</span><br />
<b>{{ translate('serverStats', 'description', data['lang']) }}:</b> <span id="input_motd" style="max-width: 10px; max-height: 10px" class="input_motd">{{ data['server_stats']['desc'] }}</span> <br />
<b>{{ translate('serverStats', 'version', data['lang']) }}:</b> <span id="version">{{
data['server_stats']['version'] }}</span><br />
<b>{{ translate('serverStats', 'description', data['lang']) }}:</b> <span id="input_motd"
style="max-width: 10px; max-height: 10px" class="input_motd">{{ data['server_stats']['desc'] }}</span>
<br />
{% else %}
<b>{{ translate('serverStats', 'version', data['lang']) }}:</b> <span id="version">{{ translate('serverStats', 'unableToConnect', data['lang']) }}</span> <br />
<b>{{ translate('serverStats', 'description', data['lang']) }}:</b> <span style="max-width: 10px; max-height: 10px" id="input_motd" class="input_motd">{{ translate('serverStats', 'unableToConnect', data['lang']) }}</span> <br />
<b>{{ translate('serverStats', 'version', data['lang']) }}:</b> <span id="version">{{
translate('serverStats', 'unableToConnect', data['lang']) }}</span> <br />
<b>{{ translate('serverStats', 'description', data['lang']) }}:</b> <span
style="max-width: 10px; max-height: 10px" id="input_motd" class="input_motd">{{ translate('serverStats',
'unableToConnect', data['lang']) }}</span> <br />
{% end %}
<b>Server Type: <span class="text-info">{{data['server_stats']['server_type']}}</span></b>
@ -64,6 +85,15 @@
seconds: duration._data.seconds
}
if (Math.round(duration._data.days)) {
obj = {
days: Math.round(duration._data.days),
hours: Math.round(duration._data.hours -= duration._data.days * 24),
minutes: duration._data.minutes,
seconds: duration._data.seconds
}
}
output = Object.entries(obj)
.map(([type, num]) => {
// make them strings
@ -135,9 +165,9 @@
server_input_motd = document.getElementById('input_motd');
/* TODO Update each element */
if (server.running){
server_status.setAttribute("class", "text-success");
server_status.innerHTML = `{{ translate('serverStats', 'online', data['lang']) }}`;
if (server.running) {
server_status.setAttribute("class", "text-success");
server_status.innerHTML = `{{ translate('serverStats', 'online', data['lang']) }}`;
startedUTC = server.started;
startedUTC = moment.utc(startedUTC, 'YYYY-MM-DD HH:mm:ss');
@ -156,9 +186,8 @@
uptimeLoop = setInterval(calculateUptime, 1000);
}
}
else
{
if (server.crashed){
else {
if (server.crashed) {
server_status.setAttribute("class", "text-danger");
server_status.innerHTML = `<i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}`;
server_started.setAttribute("class", "text-danger");
@ -167,37 +196,33 @@
uptimeLoop = null;
server_uptime.setAttribute("class", "text-danger");
server_uptime.innerHTML = `<i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}`;
}else{
server_status.setAttribute("class", "text-warning");
server_status.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
server_started.setAttribute("class", "text-warning");
server_started.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
clearInterval(uptimeLoop);
uptimeLoop = null;
server_uptime.setAttribute("class", "text-warning");
server_uptime.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
} else {
server_status.setAttribute("class", "text-warning");
server_status.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
server_started.setAttribute("class", "text-warning");
server_started.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
clearInterval(uptimeLoop);
uptimeLoop = null;
server_uptime.setAttribute("class", "text-warning");
server_uptime.innerHTML = `{{ translate('serverStats', 'offline', data['lang']) }}`;
}
}
server_cpu.innerHTML = server.cpu + ` %`;
server_mem.innerHTML = server.mem;
if (server.int_ping_results)
{
if (server.int_ping_results) {
server_players.innerHTML = server.online + `/` + server.max;
}
else
{
else {
server_players.innerHTML = `0/0`;
}
if (server.version)
{
if (server.version) {
server_version.innerHTML = server.version;
server_input_motd.innerHTML = server.desc;
}
else
{
else {
server_version.innerHTML = `{{ translate('serverStats', 'unableToConnect', data['lang']) }}`;
server_input_motd.innerHTML = `{{ translate('serverStats', 'unableToConnect', data['lang']) }}`;
}
@ -213,4 +238,4 @@
webSocket.on('update_server_details', update_server_details);
//}
});
</script>
</script>

View File

@ -111,7 +111,7 @@
{% block js %}
<script>
const serverId = new URLSearchParams(document.location.search).get('id')
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
function getCookie(name) {
@ -145,7 +145,7 @@
$.ajax({
type: "POST",
headers: { 'X-XSRFToken': token },
url: '/ajax/send_command?id=1',
url: '/ajax/send_command?id=' + serverId,
data: { command },
success: function (data) {
console.log("got response:");

View File

@ -306,6 +306,24 @@
$(document).ready(function () {
try {
if ($('#backup_path').val() == '') {
console.log('true')
try {
document.getElementById('backup_now_button').disabled = true;
} catch {
}
} else {
document.getElementById('backup_now_button').disabled = false;
}
} catch {
try {
document.getElementById('backup_now_button').disabled = false;
} catch {
}
}
console.log("ready!");
$("#backup_config_box").hide();
$("#backup_save_note").hide();

File diff suppressed because it is too large Load Diff

View File

@ -44,11 +44,18 @@
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-calendar"></i> Scheduled Tasks</h4>
<span class="too_small" title="{{ translate('serverSchedules', 'cannotSee', data['lang']) }}" , data-content="{{ translate('serverSchedules', 'cannotSeeOnMobile', data['lang']) }}" , data-placement="bottom"></span>
<div><button onclick="location.href=`/panel/add_schedule?id={{ data['server_stats']['server_id']['server_id'] }}`" class="btn btn-info">Create New Schedule <i class="fas fa-pencil-alt"></i></button></div>
{% if data['user_data']['hints'] %}
<span class="too_small" title="{{ translate('serverSchedules', 'cannotSee', data['lang']) }}" ,
data-content="{{ translate('serverSchedules', 'cannotSeeOnMobile', data['lang']) }}" ,
data-placement="bottom"></span>
{% end %}
<div><button
onclick="location.href=`/panel/add_schedule?id={{ data['server_stats']['server_id']['server_id'] }}`"
class="btn btn-info">Create New Schedule <i class="fas fa-pencil-alt"></i></button></div>
</div>
<div class="card-body">
<table class="table table-hover d-none d-lg-block responsive-table" id="schedule_table" width="100%" style="table-layout:fixed;">
<table class="table table-hover d-none d-lg-block responsive-table" id="schedule_table" width="100%"
style="table-layout:fixed;">
<thead>
<tr class="rounded">
<th style="width: 2%; min-width: 10px;">ID</th>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
# Generated by database migrator
import peewee
def migrate(migrator, database, **kwargs):
migrator.add_columns("users", hints=peewee.BooleanField(default=True))
"""
Write your migrations here.
"""
def rollback(migrator, database, **kwargs):
migrator.drop_columns("users", ["hints"])
"""
Write your rollback migrations here.
"""

View File

@ -0,0 +1,42 @@
import peewee
import datetime
from app.classes.models.servers import Servers
def migrate(migrator, database, **kwargs):
db = database
class ServerStats(peewee.Model):
stats_id = peewee.AutoField()
created = peewee.DateTimeField(default=datetime.datetime.now)
server_id = peewee.ForeignKeyField(Servers, backref="server", index=True)
started = peewee.CharField(default="")
running = peewee.BooleanField(default=False)
cpu = peewee.FloatField(default=0)
mem = peewee.FloatField(default=0)
mem_percent = peewee.FloatField(default=0)
world_name = peewee.CharField(default="")
world_size = peewee.CharField(default="")
server_port = peewee.IntegerField(default=25565)
int_ping_results = peewee.CharField(default="")
online = peewee.IntegerField(default=0)
max = peewee.IntegerField(default=0)
players = peewee.CharField(default="")
desc = peewee.CharField(default="Unable to Connect")
version = peewee.CharField(default="")
updating = peewee.BooleanField(default=False)
waiting_start = peewee.BooleanField(default=False)
first_run = peewee.BooleanField(default=True)
crashed = peewee.BooleanField(default=False)
downloading = peewee.BooleanField(default=False)
class Meta:
table_name = "server_stats"
database = db
migrator.create_table(ServerStats)
def rollback(migrator, database, **kwargs):
migrator.drop_table("server_stats")

View File

@ -176,7 +176,8 @@
"not-downloaded": "We can't seem to find your executable file. Has it finished downloading? Are the permissions set to executable?",
"portReminder": "We have detected this is the first time {} has been run. Make sure to forward port {} through your router/firewall to make this remotely accessible from the internet.",
"start-error": "Server {} failed to start with error code: {}",
"terribleFailure": "What a Terrible Failure!"
"terribleFailure": "What a Terrible Failure!",
"fileTooLarge": "Upload failed. File upload too large. Contact system administrator for assistance."
},
"footer": {
"allRightsReserved": "All rights reserved",
@ -516,7 +517,7 @@
"notExist": "You cannot delete something that doesn't exist!",
"pageTitle": "Edit User",
"pageTitleNew": "Create User",
"password": "Password",
"password": "New Password",
"permName": "Permission Name",
"repeat": "Repeat Password",
"roleName": "Role Name",

532
app/translations/it_IT.json Normal file
View File

@ -0,0 +1,532 @@
{
"404": {
"contact": "Contatta il supporto di Crafty Control tramite Discord",
"unableToFind": "Non siamo riusciti a trovare la pagina che cercavi. Per favore prova di nuovo, oppure torna indietro e riprova.",
"notFound": "Pagina non trovata"
},
"accessDenied": {
"accessDenied": "Accesso negato",
"contact": "Contatta il supporto di Crafty Control tramite Discord",
"contactAdmin": "Contatta l'amministratore del tuo server per l'accesso a questa risorsa, o se pensi tu debba aver accesso a questa risorsa, contatta il supporto tecnico.",
"noAccess": "Non hai accesso a questa risorsa"
},
"apiKeys": {
"apiKeys": "Chiavi API",
"auth": "Autorizzato? ",
"buttons": "Pulsanti",
"config": "Configura",
"crafty": "Crafty: ",
"created": "Creato",
"createNew": "Crea un nuovo Token API",
"deleteKeyConfirmation": "Vuoi cancellare questa chiave API? Non puoi tornare indietro.",
"deleteKeyConfirmationTitle": "Rimuovere la chiave API ${keyId}?",
"getToken": "Prendi un Token",
"name": "Nome",
"nameDesc": "Come desideri chiamare questo Token API? ",
"no": "No",
"pageTitle": "Modifica le chiavi API degli utenti",
"permName": "Nome Permesso",
"perms": "Permessi",
"server": "Server: ",
"superUser": "Super User",
"yes": "Sì"
},
"base": {
"doesNotWorkWithoutJavascript": "<strong>Avviso: </strong>Crafty non funziona a dovere se JavaScript è disabilitato!"
},
"credits": {
"developmentTeam": "Squadra di Svuluppo",
"hugeDesc": "Un enorme",
"pageDescription": "Senza queste persone, non avremmo Crafty",
"pageTitle": "Crediti",
"patreonDesc": "ai nostri supporter di Patreon!",
"patreonLevel": "Livello",
"patreonName": "Nome",
"patreonOther": "Altro",
"patreonSupporter": "Supporter Patreon",
"patreonUpdate": "Ultimo aggiornamento:",
"retiredStaff": "Staff ritirato",
"supportTeam": "Squadra di supporto e documentazione",
"thankYou": "GRAZIE",
"translationDesc": "alla nostra community di traduttori!",
"translationName": "Nome",
"translationTitle": "Traduttore in lingua",
"translator": "Traduttori"
},
"dashboard": {
"dashboard": "Pannello di Controllo",
"memUsage": "Utilizzo della memoria",
"cpuUsage": "Utilizzo del Processore",
"host": "Host",
"players": "Giocatori",
"backups": "Backups",
"newServer": "Crea un Nuovo Server",
"allServers": "Tutti i Server",
"server": "Server",
"actions": "Azioni",
"size": "Dimensioni della Cartella del Server",
"motd": "MOTD",
"version": "Versione",
"status": "Stato",
"online": "Online",
"offline": "Offline",
"lastBackup": "Ultimo:",
"nextBackup": "Prossimo:",
"servers": "Servers",
"cannotSeeOnMobile": "Non vedi nulla da mobile?",
"cannotSee": "Non vedi qualcosa?",
"cannotSeeOnMobile2": "Prova a scorrere la tabella orizzontalmente.",
"max": "Massimo",
"avg": "Media",
"bePatientStart": "Per favore sii paziente mentre avviamo il server.<br /> Questa schermata si aggiornerà a breveThis screen will refresh in a moment",
"bePatientStop": "Per favore sii paziente while we stop the server.<br /> Questa schermata si aggiornerà a breve",
"bePatientRestart": "Per favore sii paziente mentre riavviamo il server.<br /> Questa schermata si aggiornerà a breve",
"bePatientClone": "Per favore sii paziente mentre cloniamo il server.<br /> Questa schermata si aggiornerà a breve",
"sendingCommand": "Invio il tuo comando",
"cpuCurFreq": "Velocità attuale CPU",
"cpuMaxFreq": "Velocità massima CPU",
"cpuCores": "CPU Cores",
"start": "Start",
"stop": "Stop",
"clone": "Clona",
"kill": "Termina il Processo",
"restart": "Riavvia",
"killing": "Terminando il processo...",
"starting": "Avvio ritardato",
"delay-explained": "Il servizio/agente è stato avviato di recente e sta ritardando l'avvio del server di Minecraft",
"no-servers": "Attualmente non ci sono server. Per cominciare, cliccalick",
"welcome": "Benvenuto su Crafty Controller",
"crashed": "Crashato"
},
"datatables": {
"i18n": {
"decimal": "",
"emptyTable": "Nessun dato disponibile nella tabella",
"info": "Mostro da _START_ a _END_ di _TOTAL_ record",
"infoEmpty": "Mostro da 0 a 0 di 0 record",
"infoFiltered": "(filtrato da _MAX_ record totali)",
"infoPostFix": "",
"thousands": ",",
"lengthMenu": "Mostra _MENU_ record",
"loadingRecords": "Carico...",
"processing": "Calcolo...",
"search": "Cerca:",
"zeroRecords": "Nessun record corrispondente trovato",
"paginate": {
"first": "Prima",
"last": "Ultima",
"next": "Prossima",
"previous": "Precedente"
},
"aria": {
"sortAscending": ": attiva per ordinare le colonne in modo ascendente",
"sortDescending": ": attiva per ordinare le colonne in modo discendente"
},
"buttons": {
"collection": "Collection <span class='ui-button-icon-primary ui-icon ui-icon-triangle-1-s'\/>",
"colvis": "Visibilità colonne",
"colvisRestore": "Ripristina visibilità",
"copy": "Copy",
"copyKeys": "Premi ctrl or u2318 + C per copiare i dati della tabella negli appunti di sistema.<br><br>Per cancellare, clicca questo messaggio o premi esc.",
"copySuccess": {
"1": "Copiata 1 riga negli appunti",
"_": "Copiate %d righe negli appunti"
},
"copyTitle": "Copia negli appunti di sistema",
"csv": "CSV",
"excel": "Excel",
"pageLength": {
"-1": "Mostra tutte le righe",
"1": "Mostra 1 riga",
"_": "Mostra %d righe"
},
"pdf": "PDF",
"print": "Stampa"
},
"select": {
"rows": {
"0": "Clicca su una riga per selezionarla",
"1": "%d riga selezionata",
"_": "%d righe selezionate"
},
"cells": {
"0": "Clicca su una cella per selezionarla",
"1": "%d cella selezionata",
"_": "%d celle selezionate"
},
"columns": {
"0": "Clicca su una colonna per selezionarla",
"1": "%d colonna selezionata",
"_": "%d colonne selezionate"
}
}
}
},
"error": {
"contact": "Contact Crafty Control Support via Discord",
"embarassing": "Oh my, well, this is embarrassing.",
"error": "Error!",
"eulaAgree": "Do you agree?",
"eulaMsg": "You must agree to the EULA. A copy of the Mojang EULA is linked under this message.",
"eulaTitle": "Agree To EULA",
"hereIsTheError": "Here is the error",
"internet": "We have detected the machine running Crafty has no connection to the internet. Client connections to the server may be limited.",
"no-file": "We can't seem to locate the requested file. Double check the path. Does Crafty have proper permissions?",
"noJava": "Server {} failed to start with error code: We have detected Java is not installed. Please install java then start the server.",
"not-downloaded": "We can't seem to find your executable file. Has it finished downloading? Are the permissions set to executable?",
"portReminder": "We have detected this is the first time {} has been run. Make sure to forward port {} through your router/firewall to make this remotely accessible from the internet.",
"start-error": "Server {} failed to start with error code: {}",
"terribleFailure": "What a Terrible Failure!"
},
"footer": {
"allRightsReserved": "Tutti i diritti Riservati",
"copyright": "Copyright",
"version": "Versione"
},
"login": {
"forgotPassword": "Password dimenticata",
"login": "Accedi",
"password": "Password",
"username": "Nome utente"
},
"notify": {
"activityLog": "Registri di Attività",
"backupComplete": "Il Backup per il server {} è stato completato correttamente",
"backupStarted": "Il Backup per il server {} è cominciato",
"downloadLogs": "Scaricare i registri di Supporto?",
"finishedPreparing": "Abbiamo finito di preparare i tuoi registri di supporto.Per favore clicca Scarica per scaricarli",
"logout": "Disconnessione",
"preparingLogs": "Per favore aspetta mentre prepariamo i tuoi registri... Ti invieremo una notifica quando saranno pronti. Potrebbe volerci un po' per installazioni grosse.",
"supportLogs": "Registri di supporto"
},
"panelConfig": {
"adminControls": "Controllo admin",
"allowedServers": "Server permessi",
"assignedRoles": "Ruoli assegnati",
"cancel": "Cancella",
"clearComms": "Pulisci i comandi non eseguiti",
"delete": "Elimina",
"edit": "Modifica",
"enabled": "Abilitato",
"newRole": "Aggiungi nuovo ruolo",
"newUser": "Aggiungi nuovo utente",
"pageTitle": "Configurazioni del pannello",
"role": "Ruolo",
"roles": "Ruoli",
"roleUsers": "Utenti del ruolo",
"save": "Salva",
"superConfirm": "Procedi solo se vuoi che questo utente abbia accesso a TUTTO (tutti gli utenti, server, impostazioni del pannello, ecc...). Può anche revocare i tuoi poteri da superuser.",
"superConfirmTitle": "Abilitare lo superuser? Sei sicuro?",
"user": "Utente",
"users": "Utenti"
},
"rolesConfig": {
"config": "Configura ruolo",
"configDesc": "Qui è dove puoi modificare la configurazione del ruolo",
"configUpdate": "Ultimo aggiornamento: ",
"created": "Creato il: ",
"delRole": "Elimina ruolo",
"doesNotExist": "Non puoi eliminare qualcosa che ancora non esiste",
"pageTitle": "Modifica ruolo",
"pageTitleNew": "Nuovo ruolo",
"permAccess": "Abilitato?",
"permName": "Nome permesso",
"permsServer": "Permessi che questo ruolo ha per questi specifici server",
"roleConfigArea": "Area di configurazione del ruolo",
"roleDesc": "Come vorresti chiamare questo ruolo?",
"roleName": "Nome del ruolo: ",
"rolePerms": "Permessi del ruolo",
"roleServers": "Server abilitati",
"roleTitle": "Impostazioni del ruolo",
"roleUserName": "Nome Utente",
"roleUsers": "Utenti con il ruolo: ",
"serverAccess": "Abilitato?",
"serverName": "Nome del Server",
"serversDesc": "Server a cui questo ruolo è consentito l'accesso"
},
"serverBackups": {
"backupAtMidnight": "Auto-backup a mezzanotte?",
"backupNow": "Effettua il Backup Ora!",
"backupTask": "Un'azione di backup è cominciata.",
"cancel": "Cancella",
"clickExclude": "Clicca per selezionare le esclusioni",
"compress": "Comprimi backup",
"confirm": "Conferma",
"confirmDelete": "Vuoi eliminare questo backup? Non puoi tornare indietro.",
"confirmRestore": "Sei sicuro di voler ripristinare qeusto backup? Tutti i file correnti verranno sovrascritti allo stato di backup e saranno irrecuperabili.",
"currentBackups": "Backup attuali",
"delete": "Elimina",
"destroyBackup": "Distruggere il backup \" + file_to_del + \"?",
"download": "Scarica",
"excludedBackups": "Percorsi esclusi: ",
"excludedChoose": "Scegli i percorsi che desideri escludere dai tuoi backups",
"exclusionsTitle": "Fai un backup delle esclusioni",
"maxBackups": "Backup massimi",
"maxBackupsDesc": "Crafty non memorizzerà più di N backup, cancellando quelli più vecchi (inserisci 0 per mantenerli tutti)",
"options": "Opzioni",
"path": "Percorso",
"restore": "Ripristina",
"restoring": "Ripristinando il backup. Potrebber volerci un momento. Per favore sii paziente.",
"save": "Salva",
"size": "Dimensioni",
"storageLocation": "Percorso di memorizzazione",
"storageLocationDesc": "Dove vuoi memorizzare i backup?"
},
"serverConfig": {
"bePatientDelete": "Per favore sii paziente mentre rimuoviamo il tuo server dal pannello di Crafty. Questa schermata si chiuderà in pochi istanti.",
"bePatientDeleteFiles": "Per favore sii paziente mentre rimuoviamo il tuo server dal pannello di Crafty e cancelliamo tutti i files. Questa schermata si chiuderà in pochi istanti.",
"bePatientUpdate": "Per favore sii paziente mentre aggiorniamo il server. I tempi di download possono variare dalla tua velocità di internet.<br /> Questa schermata si aggiornerà a breve",
"cancel": "Cancella",
"crashTime": "Crash Timeout",
"crashTimeDesc": "Quanto dobbiamo aspettare per considerare il tuo server crashato per colpa di un timeout?",
"deleteFilesQuestion": "Eliminare i file del server dalla macchina?",
"deleteFilesQuestionMessage": "Vuoi che crafty cancelli tutti i file del server dalla macchina? <br><br><strong>Questo include i backup del server.</strong>",
"deleteServer": "Eliminare il server",
"deleteServerQuestion": "Eliminare il server?",
"deleteServerQuestionMessage": "Sei sicuro di voler eliminare questo server? Dopo la conferma non puoi tornare indietro...",
"exeUpdateURL": "URL di aggiornamento dell'eseguibile del server",
"exeUpdateURLDesc": "URL di download diretto per gli aggiornamenti del server.",
"noDelete": "No, torna indietro",
"noDeleteFiles": "No, eliminalo dal pannello di controllo e basta",
"removeOldLogsAfter": "Rimuovi i registri vecchi dopo",
"removeOldLogsAfterDesc": "Di quanti giorni dovrà essere vecchio un registro per essere cancellato (inserendo 0 non verranno cancellati)",
"save": "Salva",
"sendingDelete": "Elimino il server",
"sendingRequest": "Invio la tua richiesta...",
"serverAutoStart": "Avvio automatico del server",
"serverAutostartDelay": "Ritardo d'avvio automatico",
"serverAutostartDelayDesc": "Ritardo in secondi per l'avvio automatico (se abilitato qui sotto)",
"serverCrashDetection": "Rilevamento dei crash del Server",
"serverExecutable": "Eseguibile del server",
"serverExecutableDesc": "il file Eseguibile del server",
"serverExecutionCommand": "Comando d'esecuzione del server",
"serverExecutionCommandDesc": "Quello che verrà lanciato in un terminale nascosto",
"serverIP": "IP del server",
"serverIPDesc": "L'IP a cui Crafty si dovrà collegare per le statistiche (Prova un IP reale se 127.0.0.1 causa problemi)",
"serverLogLocation": "Percorso dei Registri",
"serverLogLocationDesc": "Percorso assoluto dei file di Registro",
"serverName": "Nome del Server",
"serverNameDesc": "Come desideri chiamare questo Server",
"serverPath": "Cartella operativa del server",
"serverPathDesc": "Percorso assoluto completo (non includere l'eseguibile)",
"serverPort": "Porta del server",
"serverPortDesc": "La Porta a cui Crafty si dovrà collegare per le statistiche",
"serverStopCommand": "Comando d'arresto del server",
"serverStopCommandDesc": "Comando inviato al server per l'arresto",
"stopBeforeDeleting": "Per favore arresta il server prima di eliminarlo",
"update": "Aggiorna l'eseguibile",
"yesDelete": "Sì, elimina",
"yesDeleteFiles": "Sì, elimina i files"
},
"serverConfigHelp": {
"desc": "Qui è dove puoi cambiare la configurazione del tuo server",
"perms": [
"Raccomandiamo di <code>NON</code> cambiare il percorso di un server gestito da Crafty.",
"Modificare il percorso <code>POTREBBE</code> rompere cose, specialmente su sistemi operativi Linux, dove i permessi dei file sono più stringenti.",
"<br /><br/>",
"Se senti di dover cambiare la posizione di un server, puoi farlo, ma dovrai dare all'utente \"crafty\" i permessi di leggere e scrivere nel nuovo percorso.",
"<br />",
"<br />",
"Su Linux puoi farlo con questi comandi:<br />",
"<code>",
" sudo chown crafty:crafty /path/to/your/server -R<br />",
" sudo chmod 2775 /path/to/your/server -R<br />",
"</code>"
],
"title": "Area di configurazione del server"
},
"serverDetails": {
"backup": "Backup",
"config": "Configura",
"files": "Files",
"logs": "Registri",
"playerControls": "Gestisci i giocatori",
"schedule": "Programma",
"serverDetails": "Dettagli del Server",
"terminal": "Terminale"
},
"serverFiles": {
"clickUpload": "Clicca qui per selezionare i file",
"close": "Chiudi",
"createDir": "Crea Cartella",
"createDirQuestion": "Che nome vuoi dare alla nuova cartella?",
"createFile": "Crea file",
"createFileQuestion": "Che nome vuoi dare al nuovo file?",
"default": "Default",
"delete": "Elimina",
"deleteItemQuestion": "Sei sicuro di voler eliminare \" + name + \"?",
"deleteItemQuestionMessage": "Stai eliminando \\\"\" + path + \"\\\"!<br/><br/>Questa azione è irreversible e sarà perso per sempre!",
"download": "Scarica",
"editingFile": "Modificando i files",
"error": "Si è verificato un errore nel recuperare i files",
"fileReadError": "Errore nella lettura del file",
"files": "Files",
"keybindings": "Scorciatoie da tastiera",
"loadingRecords": "Caricando i files...",
"noDelete": "No",
"noscript": "Il gestore file non funziona senza JavaScript",
"rename": "Rinomina",
"renameItemQuestion": "Quale sarà il nuovo nome?",
"save": "Salva",
"stayHere": "NON ABBANDONARE QUESTA PAGINA!",
"unsupportedLanguage": "Avviso: Questo non è un tipo di file supportato",
"unzip": "Scompatta",
"upload": "Carica",
"uploadTitle": "Carica i Files su: ",
"waitUpload": "Per favore aspetta mentre carichiamo i tuoi files... Potrebbe volerci un momento.",
"yesDelete": "Sì, conosco le conseguenze"
},
"serverPlayerManagement": {
"bannedPlayers": "Giocatori Banditi",
"loadingBannedPlayers": "Carico i giocatori banditi",
"players": "Giocatori"
},
"serverScheduleConfig": {
"backup": "Fai un Backup del Server",
"basic": "Basic",
"children": "Azioni figlie legate a questa: ",
"command": "Comando",
"command-explain": "Quale comando vuoi che eseguiamo? Non includere la '/'",
"cron": "Cron",
"cron-explain": "Inserisci la stringa CRON",
"custom": "Comando personalizzato",
"days": "Giorni",
"enabled": "Abilitata",
"hours": "Ore",
"interval": "Intervallo",
"interval-explain": "Ogni quanto vuoi che questa azione venga eseguita?",
"minutes": "Minuti",
"offset": "Offset di ritardo",
"offset-explain": "Quanto dobbiamo aspettare prima di eseguire questo dopo aver eseguito la prima azione? (Secondi)",
"one-time": "Elimina dopo l'esecuzione",
"parent": "Seleziona un'azione programmata padre",
"parent-explain": "Quale azione dovrebbe far eseguire questa?",
"reaction": "Reazione",
"restart": "Riavvia il Server",
"start": "Avvia il server",
"stop": "Arresta il Server",
"time": "Orario",
"time-explain": "A che ora vuoi eseguire la tua azione programmata?"
},
"serverSchedules": {
"areYouSure": "Eliminare l'azione programmata?",
"cancel": "Cancella",
"cannotSee": "Non vedi tutto?",
"cannotSeeOnMobile": "Prova a cliccare su un'azione programmata per tutti i dettagli.",
"confirm": "Conferma",
"confirmDelete": "Vuoi eliminare l'azione programmata? Non puoi tornare indietro."
},
"serverStats": {
"cpuUsage": "Utilizzo del Processore",
"description": "Descrizione",
"errorCalculatingUptime": "Errore nel calcolo dei tempi di operazione",
"memUsage": "Utilizzo della memoria",
"offline": "Offline",
"online": "Online",
"players": "Giocatori",
"serverStarted": "Server Avviato",
"serverStatus": "Stato del Server",
"serverTime": "UTC Time",
"serverTimeZone": "Timezone del Server",
"serverUptime": "Tempo di operazione del Server",
"starting": "Avvio ritardato",
"unableToConnect": "Impossibile connettersi",
"version": "Versione"
},
"serverTerm": {
"commandInput": "Inserisci il comando",
"delay-explained": "Il servizio/agente è stato avviato di recente e sta ritardando l'avvio del server di Minecraft",
"downloading": "Scaricando...",
"restart": "Riavvia",
"sendCommand": "Invia il comando",
"start": "Avvia",
"starting": "Avvio ritardato",
"stop": "Ferma",
"stopScroll": "Ferma lo scorrimento automatico",
"updating": "Aggiornando..."
},
"serverWizard": {
"absoluteServerPath": "Percorso Assoluto al tuo Server",
"absoluteZipPath": "Percorso Assoluto del Server",
"addRole": "Aggiungi il Server a Ruoli Esistenti",
"autoCreate": "Se non sono selezionati, Crafty ne creerà per te!",
"bePatient": "Per favore attendi mentre ' + (importing ? 'importiamo' : 'scarichiamo') + ' il Server",
"buildServer": "Compila il Server!",
"clickRoot": "Clicca qui per selezionare la Directory Radice",
"close": "Chiudi",
"defaultPort": "25565 default",
"downloading": "Scaricando il Server...",
"explainRoot": "Per favore clicca il pulsante qui sotto per selezionare la cartella radice del server all'interno dell'archivio",
"importing": "Importando il Server...",
"importServer": "Importa un Server Esistente",
"importServerButton": "Importa Server!",
"importZip": "Importa da un File Zip",
"maxMem": "Memoria Massima",
"minMem": "Memoria minima",
"myNewServer": "Il mio nuovo Server",
"newServer": "Crea un Nuovo Server",
"quickSettings": "Impostazioni Rapide",
"quickSettingsDescription": "Non ti preoccupare, puoi cambiarle più tardi",
"resetForm": "Reset Form",
"save": "Salva",
"selectRole": "Seleziona i Ruoli",
"selectRoot": "Seleziona la directory radice dell'archivio",
"selectType": "Seleziona un Tipo",
"selectVersion": "Seleziona una versione",
"selectZipDir": "Seleziona il percorso in memoria in cui scompatteremo i file",
"serverJar": "File eseguibile del Server",
"serverName": "Nome Server",
"serverPath": "Percorso del Server",
"serverPort": "Porta del Server",
"serverType": "Tipo di Server",
"serverVersion": "Versione del Server",
"sizeInGB": "Dimensione in GB",
"zipPath": "Percorso del Server"
},
"sidebar": {
"contribute": "Contribuisci",
"credits": "Crediti",
"dashboard": "Pannello di controllo",
"documentation": "Documentazione",
"navigation": "Navigazione",
"newServer": "Crea un Nuovo Server",
"servers": "Servers"
},
"userConfig": {
"apiKey": "Chiavi API",
"auth": "Autorizzato? ",
"config": "Configura",
"configArea": "Area di configurazione utente",
"configAreaDesc": "Qui è dove modifichi tutte le impostazioni utente",
"confirmDelete": "Sei sicuro di voler eliminare qeusto utente? Quest'azione è irreversibile.",
"craftyPermDesc": "I permessi Crafty che questo utente possiede ",
"craftyPerms": "Permessi di Crafty: ",
"created": "Creato il: ",
"deleteUser": "Elimina Utente: ",
"deleteUserB": "Elimina Utente",
"delSuper": "Non puoi eliminare uno superuser",
"enabled": "Abilitato",
"gravDesc": "Questa email è solamente per l'uso di Gravatar™. Crafty non userà l'email in alcuna circostanza, se non per associare il Gravatar™",
"gravEmail": "Gravatar™ Email",
"lastIP": "Ultimo indirizzo IP: ",
"lastLogin": "Ultimo accesso: ",
"lastUpdate": "Ultimo aggiornamento: ",
"leaveBlank": "Per modificare l'utente senza cambiare la passowrd, lascia il campo in bianco.",
"member": "È membro?",
"notExist": "Non puoi eliminare qualcosa che non esiste!",
"pageTitle": "Modifica Utente",
"pageTitleNew": "Crea Utente",
"password": "Password",
"permName": "Nome del Permesso",
"repeat": "Ripeti la Password",
"roleName": "Nome del ruolo",
"super": "Super User",
"userLang": "Linguaggio dell'utente",
"userName": "Nome utente",
"userNameDesc": "Come vuoi chiamare questo utente?",
"userRoles": "Ruoli dell'utente",
"userRolesDesc": "Ruoli di cui l'utente fa parte.",
"userSettings": "Impostazioni Utente",
"uses": "Numero di usi permessi (-1==Nessun limite)"
}
}

View File

@ -1,347 +0,0 @@
{
"404": {
"contact": "Contatta l'assistenza di Crafty Controller su Discord!",
"notFound": "Pagina non trovata",
"unableToFind": "We were unable to find the page you are looking for. Please try again, or go back and refresh."
},
"accessDenied": {
"accessDenied": "Accesso negato",
"contact": "Contatta il supporto di Crafty Control tramite Discord",
"contactAdmin": "Contatta l'amministratore del tuo server per accedere a questa risorsa, o, se pensi tu debba avere accesso a tale risorsa, contatta il supporto.",
"noAccess": "Non hai accesso a questa risorsa"
},
"base": {
"doesNotWorkWithoutJavascript": "<strong>Warning: </strong>Crafty doesn't work properly when JavaScript isn't enabled!"
},
"dashboard": {
"actions": "Azioni",
"allServers": "Tutti i Servers",
"avg": "Media",
"backups": "Backups",
"bePatientClone": "Per favore sii paziente mentre cloniamo il server.<br /> Questa schermata si aggiornerà in un momento",
"bePatientRestart": "Per favore sii paziente mentre riavviamo il server.<br /> Questa schermata si aggiornerà in un momento",
"bePatientStart": "Per favore sii paziente mentre accendiamo il server.<br /> Questa schermata si aggiornerà in un momento",
"bePatientStop": "Per favore sii paziente mentre spegniamo il server.<br /> Questa schermata si aggiornerà in un momento",
"cannotSee": "Non vedi nulla?",
"cannotSeeOnMobile": "Non vedi nulla da mobile?",
"cannotSeeOnMobile2": "Prova a scorrere sulla tabella lateralmente.",
"clone": "Clona",
"cpuCores": "Core della CPU",
"cpuCurFreq": "Frequenza Attuale della CPU",
"cpuMaxFreq": "Frequenza Massima della CPU",
"cpuUsage": "Uso della CPU",
"dashboard": "Dashboard",
"delay-explained": "Il servizio è partito di recente e sta ritardando l'avvio dell'istanza del Server di Minecraft",
"host": "Host",
"kill": "Termina il processo",
"killing": "Terminando il processo...",
"lastBackup": "Ultimo:",
"max": "Massimo",
"memUsage": "Uso della Memoria",
"motd": "Slogan",
"newServer": "Crea un nuovo Server",
"nextBackup": "Prossimo:",
"no-servers": "Attualmente non ci sono server. Per cominciare, clicca",
"offline": "Offline",
"online": "Online",
"players": "Giocatori",
"restart": "Riavvia",
"sendingCommand": "Inviando il tuo comando",
"server": "Server",
"servers": "Server",
"start": "Avvia",
"starting": "Avvio ritardato",
"status": "Stato",
"stop": "Ferma",
"version": "Versione",
"welcome": "Benvenuto su Crafty Controller",
"world": "Mondo"
},
"datatables": {
"i18n": {
"aria": {
"sortAscending": ": attiva per ordinare le colonne in modo ascendente",
"sortDescending": ": attiva per ordinare le colonne in modo discendente"
},
"buttons": {
"collection": "Collezione <span class='ui-button-icon-primary ui-icon ui-icon-triangle-1-s'/>",
"colvis": "Visibilità della colonna",
"colvisRestore": "Ripristina Visibilità",
"copy": "Copia",
"copyKeys": "Premi Ctrl o u2318 + C per copiare i dati della tabella negli appunti di sistema.<br><br> Per cancellare, clicca questo messaggio o premi ESC.",
"copySuccess": {
"1": "Copiata 1 riga negli appunti",
"_": "Copiate %d righe negli appunti"
},
"copyTitle": "Copia negli appunti",
"csv": "CSV",
"excel": "Excel",
"pageLength": {
"1": "Mostra 1 riga",
"-1": "Mostra tutte le righe",
"_": "Mostra %d righe"
},
"pdf": "PDF",
"print": "Print"
},
"decimal": "",
"emptyTable": "Nessun dato disponibile nella tabella",
"info": "Visualizzando da _START_ fino a _END_ di _TOTAL_ voci",
"infoEmpty": "Visualizzando da 0 fino a 0 di 0 voci",
"infoFiltered": "(Filtrato da_MAX_ voci totali)",
"infoPostFix": "",
"lengthMenu": "Mostra le voci di _MENU_ ",
"loadingRecords": "Caricamento...",
"paginate": {
"first": "Primo",
"last": "Ultimo",
"next": "Prossima",
"previous": "Precedente"
},
"processing": "Caricamento...",
"search": "Cerca:",
"select": {
"cells": {
"0": "Click on a cel to select it",
"1": "%d cell selected",
"_": "%d cells selected"
},
"columns": {
"0": "Click on a column to select it",
"1": "%d column selected",
"_": "%d columns selected"
},
"rows": {
"0": "Click on a row to select it",
"1": "%d row selected",
"_": "%d rows selected"
}
},
"thousands": ",",
"zeroRecords": "Nessuna voce corrispondente trovata"
}
},
"error": {
"closedPort": "Ci siamo accorti che la porta {} potrebbe non essere aperta, o un firewall potrebbe tenerla chiusa. Le connessioni remote al server potrebbero essere limitate.",
"contact": "Contatta l'assistenza di Crafty Controller su Discord!",
"embarassing": "Oh mamma, beh, questo è imbarazzante.",
"error": "Errore!",
"eulaAgree": "Accetti l'EULA?",
"eulaMsg": "Devi accettare l'EULA. Una copia dell'EULA di Mojang è allegata sotto questo messaggio.",
"eulaTitle": "Accetta il Contratto con l'Utente Finale",
"hereIsTheError": "Ecco l'errore",
"internet": "Ci siamo accorti che il server che fa girare Crafty non ha connessione a internet. Le connessioni dei Client potrebbero essere limitate.",
"start-error": "Il server {} non è partito, con codice errore: {}",
"terribleFailure": "Che crash epico!"
},
"footer": {
"allRightsReserved": "Tutti i diritti riservati",
"copyright": "Copyright",
"version": "Versione"
},
"login": {
"forgotPassword": "Password dimenticata",
"login": "Log In",
"password": "Password",
"username": "Nome utente"
},
"panelConfig": {
"cancel": "Cancella",
"delete": "Elimina",
"save": "Salva"
},
"serverBackups": {
"backupAtMidnight": "Backup automatico a mezzanotte?",
"backupNow": "Fai un Backup ora!",
"backupTask": "È cominciato un processo di backup.",
"cancel": "Cancella",
"confirm": "Conferma",
"confirmDelete": "Vuoi cancellare questo Backup? Questa azione non è annullabile.",
"currentBackups": "Backup attuali",
"delete": "Cancella",
"destroyBackup": "Cancellare backup \" + file_to_del + \"?",
"download": "Download",
"maxBackups": "Backup massimi",
"maxBackupsDesc": "Crafty non immagazzinerà più di N backup, cancellerà quelli più vecchi (inserisci 0 per mantenerli tutti)",
"options": "Opzioni",
"path": "Percorso",
"save": "Salva",
"size": "Dimensione",
"storageLocation": "Percorso del backup",
"storageLocationDesc": "Dove vuoi memorizzare i backup?"
},
"serverConfig": {
"bePatientDelete": "Per favore sii paziente mentre rimuoviamo il server dal pannello. Questa schermata si aggiornerà in un momento.",
"bePatientDeleteFiles": "Per favore sii paziente mentre rimuoviamo il server dal pannello ed eliminiamo i file. Questa schermata sparirà in un momento.",
"bePatientUpdate": "Per favore sii paziente mentre aggiorniamo il server. I tempi di scaricamento possono variare in base alla tua velocità di Internet.<br /> Questa schermata si aggiornerà in un momento.",
"cancel": "Cancella",
"deleteFilesQuestion": "Eliminare i file del Server dalla macchina?",
"deleteFilesQuestionMessage": "Vorresti che Crafty eliminasse tutti i file del Server dalla macchina Host?",
"deleteServer": "Elimina Server",
"deleteServerQuestion": "Eliminare il Server?",
"deleteServerQuestionMessage": "Sei sicuro di voler eliminare questo server? Dopo la conferma non si può tornare indietro...",
"exeUpdateURL": "URL per gli aggiornamenti dell'Eseguibile del Server",
"exeUpdateURLDesc": "URL Diretto per il download di aggiornamenti.",
"noDelete": "No, torna indietro",
"noDeleteFiles": "No, rimuovili dal pannello e basta",
"removeOldLogsAfter": "Rimuovi log più vecchi di ",
"removeOldLogsAfterDesc": "Di quanti giorni dovrebbe essere vecchio un log per essere cancellato (0 per non cancellarne)",
"save": "Salva",
"sendingDelete": "Eliminando il Server",
"sendingRequest": "Invio la tua richiesta...",
"serverAutoStart": "Avvio automatico del Server",
"serverAutostartDelay": "Ritardo Automatico dell'Avvio",
"serverAutostartDelayDesc": "Il ritardo per far partire il server (Se abilitato più sotto)",
"serverCrashDetection": "Rilevamento dei Crash del Server",
"serverExecutable": "Eseguibile del Server",
"serverExecutableDesc": "Il file Eseguibile del server",
"serverExecutionCommand": "Comando di Esecuzione del Server",
"serverExecutionCommandDesc": "Quello che verrà eseguito in un terminale nascosto",
"serverIP": "Server IP",
"serverIPDesc": "L'IP a cui Crafty dovrebbe connettersi per le statistiche (Prova un IP reale piuttosto di 127.0.0.1 se hai qualche problema)",
"serverLogLocation": "Posizione del Log del Server",
"serverLogLocationDesc": "Percorso Assoluto per Intero del File di Log",
"serverName": "Nome del Server",
"serverNameDesc": "Come vorresti chiamare questo server",
"serverPath": "Cartella di Operazioni del Server",
"serverPathDesc": "Percorso Assoluto per Intero (Escludendo l'eseguibile)",
"serverPort": "Porta del Server",
"serverPortDesc": "La Porta a cui Crafty dovrebbe connettersi per le statistiche",
"serverStopCommand": "Comando per terminare il server",
"serverStopCommandDesc": "Comando da inviare al server per fermarlo",
"stopBeforeDeleting": "Per favore ferma il Server prima di eliminarlo",
"update": "Aggiorna l'Eseguibile",
"yesDelete": "Sì, eliminalo",
"yesDeleteFiles": "Sì, cancella i file"
},
"serverConfigHelp": {
"desc": "Qui è dove puoi cambiare la configurazione del tuo Server",
"perms": [
"È raccomandabile <code>NON</code> cambiare i percorsi di un Server gestito da Crafty.",
"Cambiare i percorsi <code>PUO'</code> rompere cose, specialmente su sistemi tipo Linux dove i permessi dei file sono più restrittivi.",
"<br /><br/>",
"Se credi di dover cambiare il percorso di un server puoi farlo, a patto di dare all'utente \"crafty\" i permessi di leggere e scrivere nel percorso del server.",
"<br />",
"<br />",
"Su Linux questa operazione si esegue con questi comandi:<br />",
"<code>",
" sudo chown crafty:crafty /percorso/al/tuo/server -R<br />",
" sudo chmod 2775 /percorso/al/tuo/server -R<br />",
"</code>"
],
"title": "Area di Configurazione del Server"
},
"serverDetails": {
"backup": "Backup",
"config": "Config",
"files": "Files",
"logs": "Logs",
"playerControls": "Player Management",
"schedule": "Pianifica",
"serverDetails": "Dettagli del Server",
"terminal": "Terminale"
},
"serverFiles": {
"clickUpload": "Clicca qui per selezionare i tuoi file",
"close": "Chiudi",
"createDir": "Crea Cartella",
"createDirQuestion": "Che nome vuoi dare alla nuova cartella?",
"createFile": "Crea file",
"createFileQuestion": "Che nome vuoi dare al nuovo file?",
"default": "Default",
"delete": "Cancella",
"deleteItemQuestion": "Sei sicuro di voler eliminare \" + name + \"?",
"deleteItemQuestionMessage": "Stai eliminando \\\"\" + path + \"\\\"!<br/><br/>Questa azione è irreversibile e non potrai tornare indietro!",
"download": "Scarica",
"editingFile": "Modificare file",
"error": "Errore nel recuperare files",
"fileReadError": "Errore nel leggere i file",
"files": "Files",
"keybindings": "Combinazioni di tasti",
"noDelete": "No",
"noscript": "Il File Manager non funziona senza JavaScript",
"rename": "Rinomina",
"renameItemQuestion": "Qual è il nuovo nome?",
"save": "Salva",
"stayHere": "NON ABBANDONARE QUESTA PAGINA!",
"unsupportedLanguage": "Attenzione: Questo non è un tipo di file supportato",
"unzip": "Estrai",
"upload": "Carica",
"uploadTitle": "Carica i file: ",
"waitUpload": "Per favore attendi mentre carichiamo i tuoi file... Potrebbe volerci un po' di tempo.",
"yesDelete": "Sì, capisco le conseguenze"
},
"serverPlayerManagement": {
"bannedPlayers": "Giocatori Bannati",
"loadingBannedPlayers": "Carico i Giocatori Bannati",
"players": "Giocatori"
},
"serverStats": {
"cpuUsage": "Utilizzo della CPU",
"description": "Descrizione",
"errorCalculatingUptime": "Errore nel calcolare il periodo di esecuzione del server",
"memUsage": "Utilizzo della Memoria",
"offline": "Offline",
"online": "Online",
"players": "Giocatori",
"serverStarted": "Server Avviato",
"serverStatus": "Stato del server",
"serverTime": "Zona UTC",
"serverUptime": "Tempo di esecuzione",
"starting": "Avvio ritardato",
"unableToConnect": "Non riesco a connettermi",
"version": "Versione"
},
"serverTerm": {
"commandInput": "Inserisci il tuo comando",
"delay-explained": "Il servizio è partito di recente e sta ritardando l'avvio dell'istanza del Server di Minecraft",
"restart": "Riavvia",
"sendCommand": "Invia il comando",
"start": "Avvia",
"starting": "Avvio ritardato",
"stop": "Ferma",
"stopScroll": "Ferma lo scorrimento automatico",
"updating": "Aggiornando..."
},
"serverWizard": {
"absoluteServerPath": "Percorso assoluto del server",
"absoluteZipPath": "Percorso assoluto del tuo Server in Zip",
"addRole": "Aggiungi il Server a Ruoli Esistenti",
"autoCreate": "Se non sono selezionati, Crafty ne creerà uno!",
"bePatient": "Per favore sii paziente mentre ' + (importing ? 'importiamo' : 'scarichiamo') + ' il server",
"buildServer": "Costruisci il Server!",
"defaultPort": "25565 default",
"downloading": "Scaricando il Server...",
"importing": "Importando il Server...",
"importServer": "Importa un Server esistente",
"importServerButton": "Importa Server!",
"importZip": "Importa da un file Zip",
"maxMem": "Memoria Massima",
"minMem": "Memoria Minima",
"myNewServer": "Il mio nuovo server",
"newServer": "Crea un nuovo Server",
"quickSettings": "Impostazioni rapide",
"quickSettingsDescription": "Non preoccuparti, puoi cambiare queste impostazioni in seguito",
"resetForm": "Ripulisci il form",
"selectRole": "Seleziona il o i ruoli",
"selectType": "Seleziona un tipo",
"selectVersion": "Seleziona un Server",
"serverJar": "JarFile del server",
"serverName": "Nome Server",
"serverPath": "Percorso del Server",
"serverPort": "Porta del Server",
"serverType": "Tipo di Server",
"serverVersion": "Versione Server",
"sizeInGB": "Dimensione in GB",
"zipPath": "Percorso Server"
},
"sidebar": {
"contribute": "Contribuisci",
"credits": "Crediti",
"dashboard": "Dashboard",
"documentation": "Documentazione",
"navigation": "Navigazione",
"newServer": "Crea un nuovo Server",
"servers": "Servers"
}
}

View File

@ -0,0 +1,44 @@
#Base config made by Justman10000 and Zedifus (https://gitlab.com/Zedifus)
#Adapted for WSS by Andrew McManus https://gitlab.com/amcmanu3
#For this config you need to add the following mods
#mod_ssl
#mod_rewrite
#mod_http_upgrade
#mod_wss
<VirtualHost _default_:80>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
RewriteEngine on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>
<VirtualHost _default_:443>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
ProxyPreserveHost On
SSLProxyEngine On
SSLProxyVerify none
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
SSLProxyCheckPeerExpire off
#This is important for web sockets which are required by crafty to run!
RewriteEngine on
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule .* "wss://127.0.0.1:8443%{REQUEST_URI}" [P]
SSLCertificateFile /var/opt/minecraft/crafty4/app/config/web/certs/commander.cert.pem
SSLCertificateKeyFile /var/opt/minecraft/crafty4/app/config/web/certs/commander.key.pem
ProxyPass / https://127.0.0.1:8443/
ProxyPassReverse / https://127.0.0.1:8443/
ProxyRequests off
</VirtualHost>

View File

@ -3,8 +3,9 @@ version: '3'
services:
crafty:
container_name: crafty_commander
image: registry.gitlab.com/crafty-controller/crafty-commander:latest
container_name: crafty_container
image: registry.gitlab.com/crafty-controller/crafty-4:latest
restart: always
environment:
- TZ=Etc/UTC
ports:
@ -14,8 +15,8 @@ services:
- "19132:19132/udp" # BEDROCK
- "25500-25600:25500-25600" # MC SERV PORT RANGE
volumes:
- ./docker/backups:/commander/backups
- ./docker/logs:/commander/logs
- ./docker/servers:/commander/servers
- ./docker/config:/commander/app/config
- ./docker/import:/commander/import
- ./docker/backups:/crafty/backups
- ./docker/logs:/crafty/logs
- ./docker/servers:/crafty/servers
- ./docker/config:/crafty/app/config
- ./docker/import:/crafty/import

View File

@ -3,19 +3,20 @@ version: '3'
services:
crafty:
container_name: crafty_commander
container_name: crafty_container
build: ..
restart: always
environment:
- TZ=Etc/UTC
ports:
- "8000:8000" # HTTP
- "8443:8443" # HTTPS
- "8123:8123" # DYNMAP
- "19132:19132/udp" # BEDROCK
- "25500-25600:25500-25600" # MC SERV PORT RANGE
- "8000:8000" # HTTP
- "8443:8443" # HTTPS
- "8123:8123" # DYNMAP
- "19132:19132/udp" # BEDROCK
- "25500-25600:25500-25600" # MC SERV PORT RANGE
volumes:
- ./backups:/commander/backups
- ./logs:/commander/logs
- ./servers:/commander/servers
- ./config:/commander/app/config
- ./import:/commander/import
- ./backups:/crafty/backups
- ./logs:/crafty/logs
- ./servers:/crafty/servers
- ./config:/crafty/app/config
- ./import:/crafty/import

View File

@ -1,8 +1,8 @@
<?xml version="1.0"?>
<Container version="2">
<Name>Crafty</Name>
<Repository>crafty-controller/crafty-commander:latest</Repository>
<Registry>registry.gitlab.com/crafty-controller/crafty-commander</Registry>
<Repository>crafty-controller/crafty-4:latest</Repository>
<Registry>registry.gitlab.com/crafty-controller/crafty-4</Registry>
<Network>bridge</Network>
<MyIP/>
<Shell>sh</Shell>
@ -13,12 +13,12 @@
The username and password appear on the first launch in the Docker logs!&#xD;
Crafty 4 is the successor of Crafty Controller, the Docker image is no longer maintained on DockerHub. (now on GitLab)&#xD;
For official support join the discord: https://discord.gg/9VJPhCE&#xD;
For migration from 3.x please refer to the documentation on GitLab: https://gitlab.com/crafty-controller/crafty-commander/-/wikis/home&#xD;
For migration from 3.x please refer to the documentation on GitLab: https://wiki.craftycontrol.com/&#xD;
</Overview>
<Category>GameServers: Other:</Category>
<WebUI>https://[IP]:[PORT:8443]/</WebUI>
<TemplateURL>https://gitlab.com/crafty-controller/crafty-commander/-/blob/dev/docker/unraid.xml</TemplateURL>
<Icon>https://gitlab.com/crafty-controller/crafty-commander/-/raw/master/app/frontend/static/assets/images/logo_square.jpg</Icon>
<TemplateURL>https://gitlab.com/crafty-controller/crafty-4/-/blob/dev/docker/unraid.xml</TemplateURL>
<Icon>https://gitlab.com/crafty-controller/crafty-4/-/raw/master/app/frontend/static/assets/images/logo_square.jpg</Icon>
<ExtraParams/>
<PostArgs/>
<CPUset/>
@ -29,7 +29,7 @@ For migration from 3.x please refer to the documentation on GitLab: https://gitl
The username and password appear on the first launch in the Docker logs!&#xD;
Crafty 4 is the successor of Crafty Controller, the Docker image is no longer maintained on DockerHub. (now on GitLab)&#xD;
For official support join the discord: https://discord.gg/9VJPhCE&#xD;
For migration from 3.x please refer to the documentation on GitLab: https://gitlab.com/crafty-controller/crafty-commander/-/wikis/home&#xD;
For migration from 3.x please refer to the documentation on GitLab: https://gitlab.com/crafty-controller/crafty-4/-/wikis/home&#xD;
</Description>
<Networking>
<Mode>bridge</Mode>
@ -64,27 +64,27 @@ For migration from 3.x please refer to the documentation on GitLab: https://gitl
<Data>
<Volume>
<HostDir>/mnt/user/appdata/Crafty-4/servers/</HostDir>
<ContainerDir>/commander/servers</ContainerDir>
<ContainerDir>/crafty/servers</ContainerDir>
<Mode>rw</Mode>
</Volume>
<Volume>
<HostDir>/mnt/user/appdata/Crafty-4/backups/</HostDir>
<ContainerDir>/commander/backups</ContainerDir>
<ContainerDir>/crafty/backups</ContainerDir>
<Mode>rw</Mode>
</Volume>
<Volume>
<HostDir>/mnt/user/appdata/Crafty-4/logs/</HostDir>
<ContainerDir>/commander/logs</ContainerDir>
<ContainerDir>/crafty/logs</ContainerDir>
<Mode>rw</Mode>
</Volume>
<Volume>
<HostDir>/mnt/user/appdata/Crafty-4/config/</HostDir>
<ContainerDir>/commander/app/config</ContainerDir>
<ContainerDir>/crafty/app/config</ContainerDir>
<Mode>rw</Mode>
</Volume>
<Volume>
<HostDir>/mnt/user/appdata/Crafty-4/import/</HostDir>
<ContainerDir>/commander/import</ContainerDir>
<ContainerDir>/crafty/import</ContainerDir>
<Mode>rw</Mode>
</Volume>
</Data>
@ -95,9 +95,9 @@ For migration from 3.x please refer to the documentation on GitLab: https://gitl
<Config Name="Minecraft ports" Target="25500-25600" Default="25500-25600" Mode="tcp" Description="Container Port: 25500-25600 yes, 100 ports for 100 possible Servers" Type="Port" Display="always-hide" Required="true" Mask="false">25500-25600</Config>
<Config Name="Port for dynmap" Target="8123" Default="8123" Mode="tcp" Description="Dynmap port" Type="Port" Display="always-hide" Required="true" Mask="false">8123</Config>
<Config Name="Port for bedrock server" Target="19132" Default="19132" Mode="udp" Description="Bedrock server port" Type="Port" Display="always-hide" Required="true" Mask="false">19132</Config>
<Config Name="Server files" Target="/commander/servers" Default="/mnt/user/appdata/Crafty-4/servers/" Mode="rw" Description="Path to the minecraft server folders" Type="Path" Display="always-hide" Required="true" Mask="false">/mnt/user/appdata/Crafty-4/servers/</Config>
<Config Name="Backup files" Target="/commander/backups" Default="/mnt/user/appdata/Crafty-4/backups/" Mode="rw" Description="Server Backups" Type="Path" Display="always-hide" Required="true" Mask="false">/mnt/user/appdata/Crafty-4/backups/</Config>
<Config Name="Server Logs" Target="/commander/logs" Default="/mnt/user/appdata/Crafty-4/logs/" Mode="rw" Description="Logs" Type="Path" Display="advanced-hide" Required="true" Mask="false">/mnt/user/appdata/Crafty-4/logs/</Config>
<Config Name="Crafty Configuration" Target="/commander/app/config" Default="/mnt/user/appdata/Crafty-4/config/" Mode="rw" Description="Path to the persistent Crafty files" Type="Path" Display="advanced-hide" Required="true" Mask="false">/mnt/user/appdata/Crafty-4/config/</Config>
<Config Name="Import folder" Target="/commander/import" Default="/mnt/user/appdata/Crafty-4/import/" Mode="rw" Description="Imports" Type="Path" Display="advanced-hide" Required="true" Mask="false">/mnt/user/appdata/Crafty-4/import/</Config>
<Config Name="Server files" Target="/crafty/servers" Default="/mnt/user/appdata/Crafty-4/servers/" Mode="rw" Description="Path to the minecraft server folders" Type="Path" Display="always-hide" Required="true" Mask="false">/mnt/user/appdata/Crafty-4/servers/</Config>
<Config Name="Backup files" Target="/crafty/backups" Default="/mnt/user/appdata/Crafty-4/backups/" Mode="rw" Description="Server Backups" Type="Path" Display="always-hide" Required="true" Mask="false">/mnt/user/appdata/Crafty-4/backups/</Config>
<Config Name="Server Logs" Target="/crafty/logs" Default="/mnt/user/appdata/Crafty-4/logs/" Mode="rw" Description="Logs" Type="Path" Display="advanced-hide" Required="true" Mask="false">/mnt/user/appdata/Crafty-4/logs/</Config>
<Config Name="Crafty Configuration" Target="/crafty/app/config" Default="/mnt/user/appdata/Crafty-4/config/" Mode="rw" Description="Path to the persistent Crafty files" Type="Path" Display="advanced-hide" Required="true" Mask="false">/mnt/user/appdata/Crafty-4/config/</Config>
<Config Name="Import folder" Target="/crafty/import" Default="/mnt/user/appdata/Crafty-4/import/" Mode="rw" Description="Imports" Type="Path" Display="advanced-hide" Required="true" Mask="false">/mnt/user/appdata/Crafty-4/import/</Config>
</Container>

77
main.py
View File

@ -5,24 +5,31 @@ import time
import argparse
import logging.config
import signal
from app.classes.shared.console import console
from app.classes.shared.helpers import helper
import peewee
from app.classes.models.users import HelperUsers
from app.classes.shared.console import Console
from app.classes.shared.helpers import Helpers
if helper.checkRoot():
console.critical(
console = Console()
helper = Helpers()
if helper.check_root():
Console.critical(
"Root detected. Root/Admin access denied. "
"Run Crafty again with non-elevated permissions."
)
time.sleep(5)
console.critical("Crafty shutting down. Root/Admin access denied.")
Console.critical("Crafty shutting down. Root/Admin access denied.")
sys.exit(0)
# pylint: disable=wrong-import-position
from app.classes.shared.main_models import installer, database
from app.classes.shared.tasks import TasksManager
from app.classes.shared.main_controller import Controller
from app.classes.shared.migration import MigrationManager
from app.classes.shared.command import MainPrompt
try:
from app.classes.models.base_model import database_proxy
from app.classes.shared.main_models import DatabaseBuilder
from app.classes.shared.tasks import TasksManager
from app.classes.shared.main_controller import Controller
from app.classes.shared.migration import MigrationManager
from app.classes.shared.command import MainPrompt
except ModuleNotFoundError as err:
helper.auto_installer_fix(err)
def do_intro():
@ -39,7 +46,7 @@ def do_intro():
{'/' * 75}
"""
console.magenta(intro)
Console.magenta(intro)
def setup_logging(debug=True):
@ -57,7 +64,7 @@ def setup_logging(debug=True):
else:
logging.basicConfig(level=logging.DEBUG)
logging.warning(f"Unable to read logging config from {logging_config_file}")
console.critical(f"Unable to read logging config from {logging_config_file}")
Console.critical(f"Unable to read logging config from {logging_config_file}")
# Our Main Starter
@ -87,7 +94,9 @@ if __name__ == "__main__":
# setting up the logger object
logger = logging.getLogger(__name__)
console.cyan(f"Logging set to: {logger.level}")
Console.cyan(f"Logging set to: {logger.level}")
peewee_logger = logging.getLogger("peewee")
peewee_logger.setLevel(logging.INFO)
# print our pretty start message
do_intro()
@ -95,15 +104,23 @@ if __name__ == "__main__":
# our session file, helps prevent multiple controller agents on the same machine.
helper.create_session_file(ignore=args.ignore)
migration_manager = MigrationManager(database)
# start the database
database = peewee.SqliteDatabase(
helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
)
database_proxy.initialize(database)
migration_manager = MigrationManager(database, helper)
migration_manager.up() # Automatically runs migrations
# do our installer stuff
fresh_install = installer.is_fresh_install()
user_helper = HelperUsers(database, helper)
installer = DatabaseBuilder(database, helper, user_helper)
FRESH_INSTALL = installer.is_fresh_install()
if fresh_install:
console.debug("Fresh install detected")
console.warning(
if FRESH_INSTALL:
Console.debug("Fresh install detected")
Console.warning(
f"We have detected a fresh install. Please be sure to forward "
f"Crafty's port, {helper.get_setting('https_port')}, "
f"through your router/firewall if you would like to be able "
@ -111,11 +128,11 @@ if __name__ == "__main__":
)
installer.default_settings()
else:
console.debug("Existing install detected")
Console.debug("Existing install detected")
# now the tables are created, we can load the tasks_manger and server controller
controller = Controller()
tasks_manager = TasksManager(controller)
# now the tables are created, we can load the tasks_manager and server controller
controller = Controller(database, helper)
tasks_manager = TasksManager(helper, controller)
tasks_manager.start_webserver()
# slowing down reporting just for a 1/2 second so messages look cleaner
@ -123,7 +140,7 @@ if __name__ == "__main__":
# init servers
logger.info("Initializing all servers defined")
console.info("Initializing all servers defined")
Console.info("Initializing all servers defined")
controller.init_all_servers()
servers = controller.list_defined_servers()
@ -139,10 +156,10 @@ if __name__ == "__main__":
tasks_manager.serverjar_cache_refresher()
logger.info("Checking Internet. This may take a minute.")
console.info("Checking Internet. This may take a minute.")
Console.info("Checking Internet. This may take a minute.")
if not helper.check_internet():
console.warning(
Console.warning(
"We have detected the machine running Crafty has no "
"connection to the internet. Client connections to "
"the server may be limited."
@ -151,7 +168,7 @@ if __name__ == "__main__":
if not controller.check_system_user():
controller.add_system_user()
Crafty = MainPrompt(tasks_manager, migration_manager)
Crafty = MainPrompt(helper, tasks_manager, migration_manager)
project_root = os.path.dirname(__file__)
controller.set_project_root(project_root)
@ -163,7 +180,7 @@ if __name__ == "__main__":
logger.info(
f"Recieved {signal.Signals(sig[0]).name} [{sig[0]}], stopping Crafty..."
)
console.info(
Console.info(
f"Recieved {signal.Signals(sig[0]).name} [{sig[0]}], stopping Crafty..."
)
tasks_manager._main_graceful_exit()
@ -177,7 +194,7 @@ if __name__ == "__main__":
except KeyboardInterrupt:
print() # for newline
logger.info("Recieved SIGINT, stopping Crafty...")
console.info("Recieved SIGINT, stopping Crafty...")
Console.info("Recieved SIGINT, stopping Crafty...")
tasks_manager._main_graceful_exit()
Crafty.universal_exit()
else:
@ -189,7 +206,7 @@ if __name__ == "__main__":
time.sleep(1)
except KeyboardInterrupt:
logger.info("Recieved SIGINT, stopping Crafty...")
console.info("Recieved SIGINT, stopping Crafty...")
Console.info("Recieved SIGINT, stopping Crafty...")
break
tasks_manager._main_graceful_exit()
Crafty.universal_exit()

View File

@ -5,7 +5,7 @@ bleach==4.1
cached_property==1.5.2
colorama==0.4
cron-validator==1.0.3
cryptography==3.4
cryptography==3.4.8
libgravatar==1.0.0
peewee==3.13
pexpect==4.8