Merge branch 'dev' into docker-zedi

This commit is contained in:
Zedifus 2021-09-01 00:48:25 +01:00
commit c1f0a420c3
20 changed files with 652 additions and 114 deletions

View File

@ -1,9 +1,12 @@
from app.classes.shared.helpers import Helpers
import struct import struct
import socket import socket
import base64 import base64
import json import json
import sys import sys
import os
import logging.config import logging.config
from app.classes.shared.console import console
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -25,7 +28,25 @@ class Server:
description = self.description description = self.description
if 'extra' in description.keys(): if 'extra' in description.keys():
for e in description['extra']: for e in description['extra']:
#Conversion format code needed only for Java Version
lines.append(get_code_format("reset"))
if "bold" in e.keys():
lines.append(get_code_format("bold"))
if "italic" in e.keys():
lines.append(get_code_format("italic"))
if "underlined" in e.keys():
lines.append(get_code_format("underlined"))
if "strikethrough" in e.keys():
lines.append(get_code_format("strikethrough"))
if "obfuscated" in e.keys():
lines.append(get_code_format("obfuscated"))
if "color" in e.keys():
lines.append(get_code_format(e['color']))
#Then append the text
if "text" in e.keys(): if "text" in e.keys():
if e['text'] == '\n':
lines.append("§§")
else:
lines.append(e['text']) lines.append(e['text'])
total_text = " ".join(lines) total_text = " ".join(lines)
@ -70,6 +91,26 @@ class Player:
def __str__(self): def __str__(self):
return self.name return self.name
def get_code_format(format_name):
root_dir = os.path.abspath(os.path.curdir)
format_file = os.path.join(root_dir, 'app', 'config', 'motd_format.json')
try:
with open(format_file, "r", encoding='utf-8') as f:
data = json.load(f)
if format_name in data.keys():
return data.get(format_name)
else:
logger.error("Format MOTD Error: format name {} does not exist".format(format_name))
console.error("Format MOTD Error: format name {} does not exist".format(format_name))
return ""
except Exception as e:
logger.critical("Config File Error: Unable to read {} due to {}".format(format_file, e))
console.critical("Config File Error: Unable to read {} due to {}".format(format_file, e))
return ""
# For the rest of requests see wiki.vg/Protocol # For the rest of requests see wiki.vg/Protocol
def ping(ip, port): def ping(ip, port):

View File

@ -19,7 +19,7 @@ class ServerProps:
s = line s = line
s1 = s[:s.find('=')] s1 = s[:s.find('=')]
if '\n' in s: if '\n' in s:
s2 = s[s.find('=')+1:s.find('\\')] s2 = s[s.find('=')+1:s.find('\n')]
else: else:
s2 = s[s.find('=')+1:] s2 = s[s.find('=')+1:]
d[s1] = s2 d[s1] = s2

View File

@ -11,6 +11,7 @@ from app.classes.shared.helpers import helper
from app.classes.shared.console import console from app.classes.shared.console import console
from app.classes.shared.models import Servers from app.classes.shared.models import Servers
from app.classes.minecraft.server_props import ServerProps from app.classes.minecraft.server_props import ServerProps
from app.classes.web.websocket_helper import websocket_helper
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -173,11 +174,11 @@ class ServerJars:
response = self._get_api_result(url) response = self._get_api_result(url)
return response return response
def download_jar(self, server, version, path): def download_jar(self, server, version, path, name):
update_thread = threading.Thread(target=self.a_download_jar, daemon=True, name="exe_download", args=(server, version, path)) update_thread = threading.Thread(target=self.a_download_jar, daemon=True, name="exe_download", args=(server, version, path, name))
update_thread.start() update_thread.start()
def a_download_jar(self, server, version, path): def a_download_jar(self, server, version, path, name):
fetch_url = "{base}/api/fetchJar/{server}/{version}".format(base=self.base_url, server=server, version=version) fetch_url = "{base}/api/fetchJar/{server}/{version}".format(base=self.base_url, server=server, version=version)
# open a file stream # open a file stream
@ -189,6 +190,8 @@ class ServerJars:
except Exception as e: except Exception as e:
logger.error("Unable to save jar to {path} due to error:{error}".format(path=path, error=e)) logger.error("Unable to save jar to {path} due to error:{error}".format(path=path, error=e))
pass pass
websocket_helper.broadcast('notification', "Executable download finished for server named: " + name)
return False return False

View File

@ -4,6 +4,7 @@ import time
import psutil import psutil
import logging import logging
import datetime import datetime
import base64
from app.classes.shared.helpers import helper from app.classes.shared.helpers import helper
@ -145,12 +146,19 @@ class Stats:
logger.info("Unable to read json from ping_obj: {}".format(e)) logger.info("Unable to read json from ping_obj: {}".format(e))
pass pass
try:
server_icon = base64.encodebytes(ping_obj.icon)
except Exception as e:
server_icon = False
logger.info("Unable to read the server icon : {}".format(e))
ping_data = { ping_data = {
'online': online_stats.get("online", 0), 'online': online_stats.get("online", 0),
'max': online_stats.get('max', 0), 'max': online_stats.get('max', 0),
'players': online_stats.get('players', 0), 'players': online_stats.get('players', 0),
'server_description': ping_obj.description, 'server_description': ping_obj.description,
'server_version': ping_obj.version 'server_version': ping_obj.version,
'server_icon': server_icon
} }
return ping_data return ping_data
@ -167,7 +175,7 @@ class Stats:
# TODO: search server properties file for possible override of 127.0.0.1 # TODO: search server properties file for possible override of 127.0.0.1
internal_ip = server_data.get('server-ip', "127.0.0.1") internal_ip = server_data.get('server_ip', "127.0.0.1")
server_port = server_settings.get('server-port', "25565") server_port = server_settings.get('server-port', "25565")
logger.debug("Pinging {} on port {}".format(internal_ip, server_port)) logger.debug("Pinging {} on port {}".format(internal_ip, server_port))
@ -210,7 +218,7 @@ class Stats:
p_stats = self._get_process_stats(server_obj.PID) p_stats = self._get_process_stats(server_obj.PID)
# TODO: search server properties file for possible override of 127.0.0.1 # TODO: search server properties file for possible override of 127.0.0.1
internal_ip = server_data.get('server-ip', "127.0.0.1") internal_ip = server_data.get('server_ip', "127.0.0.1")
server_port = server_settings.get('server-port', "25565") server_port = server_settings.get('server-port', "25565")
logger.debug("Pinging server '{}' on {}:{}".format(s.get('server_name', "ID#{}".format(server_id)), internal_ip, server_port)) logger.debug("Pinging server '{}' on {}:{}".format(s.get('server_name', "ID#{}".format(server_id)), internal_ip, server_port))
@ -247,6 +255,62 @@ class Stats:
return server_stats_list return server_stats_list
def get_raw_server_stats(self, server_id):
server_stats = {}
server = self.controller.get_server_obj(server_id)
logger.debug('Getting stats for server: {}'.format(server_id))
# get our server object, settings and data dictionaries
server_obj = self.controller.get_server_obj(server_id)
server_obj.reload_server_settings()
server_settings = self.controller.get_server_settings(server_id)
server_data = self.controller.get_server_data(server_id)
# world data
world_name = server_settings.get('level-name', 'Unknown')
world_path = os.path.join(server_data.get('path', None), world_name)
# process stats
p_stats = self._get_process_stats(server_obj.PID)
# TODO: search server properties file for possible override of 127.0.0.1
internal_ip = server_data.get('server_ip', "127.0.0.1")
server_port = server_settings.get('server-port', "25565")
logger.debug("Pinging server '{}' on {}:{}".format(server.name, internal_ip, server_port))
int_mc_ping = ping(internal_ip, int(server_port))
int_data = False
ping_data = {}
# if we got a good ping return, let's parse it
if int_mc_ping:
int_data = True
ping_data = self.parse_server_ping(int_mc_ping)
server_stats = {
'id': server_id,
'started': server_obj.get_start_time(),
'running': server_obj.check_running(),
'cpu': p_stats.get('cpu_usage', 0),
'mem': p_stats.get('memory_usage', 0),
"mem_percent": p_stats.get('mem_percentage', 0),
'world_name': world_name,
'world_size': self.get_world_size(world_path),
'server_port': server_port,
'int_ping_results': int_data,
'online': ping_data.get("online", False),
"max": ping_data.get("max", False),
'players': ping_data.get("players", False),
'desc': ping_data.get("server_description", False),
'version': ping_data.get("server_version", False),
'icon': ping_data.get("server_icon", False)
}
return server_stats
def record_stats(self): def record_stats(self):
stats_to_send = self.get_node_stats() stats_to_send = self.get_node_stats()
node_stats = stats_to_send.get('node_stats') node_stats = stats_to_send.get('node_stats')

View File

@ -97,6 +97,14 @@ class Controller:
server_obj = self.get_server_obj(server_id) server_obj = self.get_server_obj(server_id)
server_obj.reload_server_settings() server_obj.reload_server_settings()
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']
logger.warning("Unable to find server object for server id {}".format(server_id))
return False
def get_server_obj(self, server_id): def get_server_obj(self, server_id):
for s in self.servers_list: for s in self.servers_list:
if int(s['server_id']) == int(server_id): if int(s['server_id']) == int(server_id):
@ -105,6 +113,14 @@ class Controller:
logger.warning("Unable to find server object for server id {}".format(server_id)) logger.warning("Unable to find server object for server id {}".format(server_id))
return False return False
def get_server_data(self, server_id):
for s in self.servers_list:
if int(s['server_id']) == int(server_id):
return s['server_data_obj']
logger.warning("Unable to find server object for server id {}".format(server_id))
return False
@staticmethod @staticmethod
def list_defined_servers(): def list_defined_servers():
servers = db_helper.get_all_defined_servers() servers = db_helper.get_all_defined_servers()
@ -157,7 +173,7 @@ class Controller:
@staticmethod @staticmethod
def can_add_role(user_id): def can_add_role(user_id):
#TODO: Complete if we need a User Addition limit #TODO: Complete if we need a Role Addition limit
#return db_helper.can_add_in_crafty(user_id, Enum_Permissions_Crafty.Roles_Config) #return db_helper.can_add_in_crafty(user_id, Enum_Permissions_Crafty.Roles_Config)
return True return True
@ -184,14 +200,6 @@ class Controller:
server_list = db_helper.get_authorized_servers(userId) server_list = db_helper.get_authorized_servers(userId)
return server_list return server_list
def get_server_data(self, server_id):
for s in self.servers_list:
if int(s['server_id']) == int(server_id):
return s['server_data_obj']
logger.warning("Unable to find server object for server id {}".format(server_id))
return False
def list_running_servers(self): def list_running_servers(self):
running_servers = [] running_servers = []
@ -297,7 +305,7 @@ class Controller:
server_stop = "stop" server_stop = "stop"
# download the jar # download the jar
server_jar_obj.download_jar(server, version, full_jar_path) server_jar_obj.download_jar(server, version, full_jar_path, name)
new_id = self.register_server(name, server_id, server_dir, backup_path, server_command, server_file, server_log_file, server_stop) new_id = self.register_server(name, server_id, server_dir, backup_path, server_command, server_file, server_log_file, server_stop)
return new_id return new_id

View File

@ -242,7 +242,8 @@ class db_builder:
# Users.enabled: True, # Users.enabled: True,
# Users.superuser: True # Users.superuser: True
#}).execute() #}).execute()
db_shortcuts.add_user(username, password=password, superuser=True) user_id = db_shortcuts.add_user(username, password=password, superuser=True)
#db_shortcuts.update_user(user_id, user_crafty_data={"permissions_mask":"111", "server_quantity":[-1,-1,-1]} )
#console.info("API token is {}".format(api_token)) #console.info("API token is {}".format(api_token))
@ -257,6 +258,9 @@ class db_builder:
class db_shortcuts: class db_shortcuts:
#************************************************************************************************
# Generic Databse Methods
#************************************************************************************************
@staticmethod @staticmethod
def return_rows(query): def return_rows(query):
rows = [] rows = []
@ -271,6 +275,14 @@ class db_shortcuts:
return rows return rows
@staticmethod
def return_db_rows(model):
data = [model_to_dict(row) for row in model]
return data
#************************************************************************************************
# Generic Servers Methods
#************************************************************************************************
@staticmethod @staticmethod
def create_server(name: str, server_uuid: str, server_dir: str, backup_path: str, server_command: str, server_file: str, server_log_file: str, server_stop: str, server_port=25565): def create_server(name: str, server_uuid: str, server_dir: str, backup_path: str, server_command: str, server_file: str, server_log_file: str, server_stop: str, server_port=25565):
return Servers.insert({ return Servers.insert({
@ -302,6 +314,9 @@ class db_shortcuts:
except IndexError: except IndexError:
return {} return {}
#************************************************************************************************
# Servers Methods
#************************************************************************************************
@staticmethod @staticmethod
def get_all_defined_servers(): def get_all_defined_servers():
query = Servers.select() query = Servers.select()
@ -345,21 +360,14 @@ class db_shortcuts:
return server_data return server_data
@staticmethod @staticmethod
def get_user_roles_id(user_id): def get_server_friendly_name(server_id):
roles_list = [] server_data = db_helper.get_server_data_by_id(server_id)
roles = User_Roles.select().where(User_Roles.user_id == user_id) friendly_name = "{} with ID: {}".format(server_data.get('server_name', None), server_data.get('server_id', 0))
for r in roles: return friendly_name
roles_list.append(db_helper.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)
for r in roles:
roles_list.append(db_helper.get_role(r.role_id)['role_name'])
return roles_list
#************************************************************************************************
# Servers Permissions Methods
#************************************************************************************************
@staticmethod @staticmethod
def get_permissions_mask(role_id, server_id): def get_permissions_mask(role_id, server_id):
permissions_mask = '' permissions_mask = ''
@ -414,6 +422,10 @@ class db_shortcuts:
server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)[0]}) server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)[0]})
return server_data return server_data
#************************************************************************************************
# Servers_Stats Methods
#************************************************************************************************
@staticmethod @staticmethod
def get_server_stats_by_id(server_id): 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) stats = Server_Stats.select().where(Server_Stats.server_id == server_id).order_by(Server_Stats.created.desc()).limit(1)
@ -438,6 +450,32 @@ class db_shortcuts:
return False return False
return True return True
@staticmethod
def set_update(server_id, value):
try:
row = Server_Stats.select().where(Server_Stats.server_id == server_id)
except Exception as ex:
logger.error("Database entry not found. ".format(ex))
with database.atomic():
Server_Stats.update(updating=value).where(Server_Stats.server_id == server_id).execute()
@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 = get_TTL_without_player(server_id)
if (time_limit == -1) or (ttl_no_players > time_limit):
can = True
return can
#************************************************************************************************
# Crafty Permissions Methods
#************************************************************************************************
@staticmethod @staticmethod
def get_crafty_permissions_mask(user_id): def get_crafty_permissions_mask(user_id):
permissions_mask = '' permissions_mask = ''
@ -470,6 +508,9 @@ class db_shortcuts:
} }
return quantity_list return quantity_list
#************************************************************************************************
# User_Crafty Methods
#************************************************************************************************
@staticmethod @staticmethod
def get_User_Crafty(user_id): def get_User_Crafty(user_id):
try: try:
@ -488,6 +529,11 @@ class db_shortcuts:
user_crafty = db_helper.get_User_Crafty(user_id) user_crafty = db_helper.get_User_Crafty(user_id)
return user_crafty 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}).execute()
return user_crafty
@staticmethod @staticmethod
def get_created_quantity_list(user_id): def get_created_quantity_list(user_id):
user_crafty = db_helper.get_User_Crafty(user_id) user_crafty = db_helper.get_User_Crafty(user_id)
@ -519,6 +565,10 @@ class db_shortcuts:
User_Crafty.save(user_crafty) User_Crafty.save(user_crafty)
return user_crafty.created_server return user_crafty.created_server
#************************************************************************************************
# Host_Stats Methods
#************************************************************************************************
@staticmethod @staticmethod
def get_latest_hosts_stats(): def get_latest_hosts_stats():
query = Host_Stats.select().order_by(Host_Stats.id.desc()).get() query = Host_Stats.select().order_by(Host_Stats.id.desc()).get()
@ -532,16 +582,15 @@ class db_shortcuts:
if len(test) == 0: if len(test) == 0:
return token return token
#************************************************************************************************
# Users Methods
#************************************************************************************************
@staticmethod @staticmethod
def get_all_users(): def get_all_users():
query = Users.select() query = Users.select()
return query return query
@staticmethod
def get_all_roles():
query = Roles.select()
return query
@staticmethod @staticmethod
def get_user_id_by_name(username): def get_user_id_by_name(username):
if username == "SYSTEM": if username == "SYSTEM":
@ -563,56 +612,11 @@ class db_shortcuts:
else: else:
return {} return {}
@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
}).execute()
@staticmethod
def add_user_roles(user):
if type(user) == dict:
user_id = user['user_id']
else:
user_id = user.user_id
# I just copied this code from get_user, it had those TODOs & comments made by mac - Lukas
roles_query = User_Roles.select().join(Roles, JOIN.INNER).where(User_Roles.user_id == user_id)
# TODO: this query needs to be narrower
roles = set()
for r in roles_query:
roles.add(r.role_id.role_id)
user['roles'] = roles
#logger.debug("user: ({}) {}".format(user_id, user))
return user
@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}).execute()
return user_crafty
@staticmethod
def add_role_server(server_id, role_id, rs_permissions="00000000"):
servers = Role_Servers.insert({Role_Servers.server_id: server_id, Role_Servers.role_id: role_id, Role_Servers.permissions: rs_permissions}).execute()
return servers
@staticmethod @staticmethod
def user_query(user_id): def user_query(user_id):
user_query = Users.select().where(Users.user_id == user_id) user_query = Users.select().where(Users.user_id == user_id)
return user_query return user_query
@staticmethod
def user_role_query(user_id):
user_query = User_Roles.select().where(User_Roles.user_id == user_id)
query = Roles.select().where(Roles.role_id == -1)
for u in user_query:
query = Roles.select().where(Roles.role_id == u.role_id)
return query
@staticmethod @staticmethod
def get_user(user_id): def get_user(user_id):
if user_id == 0: if user_id == 0:
@ -731,6 +735,14 @@ class db_shortcuts:
return False return False
return True return True
#************************************************************************************************
# Roles Methods
#************************************************************************************************
@staticmethod
def get_all_roles():
query = Roles.select()
return query
@staticmethod @staticmethod
def get_roleid_by_name(role_name): def get_roleid_by_name(role_name):
try: try:
@ -806,17 +818,78 @@ class db_shortcuts:
return False return False
return True return True
#************************************************************************************************
# User_Roles Methods
#************************************************************************************************
@staticmethod
def get_user_roles_id(user_id):
roles_list = []
roles = User_Roles.select().where(User_Roles.user_id == user_id)
for r in roles:
roles_list.append(db_helper.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)
for r in roles:
roles_list.append(db_helper.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
}).execute()
@staticmethod
def add_user_roles(user):
if type(user) == dict:
user_id = user['user_id']
else:
user_id = user.user_id
# I just copied this code from get_user, it had those TODOs & comments made by mac - Lukas
roles_query = User_Roles.select().join(Roles, JOIN.INNER).where(User_Roles.user_id == user_id)
# TODO: this query needs to be narrower
roles = set()
for r in roles_query:
roles.add(r.role_id.role_id)
user['roles'] = roles
#logger.debug("user: ({}) {}".format(user_id, user))
return user
#************************************************************************************************
# Role_Servers Methods
#************************************************************************************************
@staticmethod
def add_role_server(server_id, role_id, rs_permissions="00000000"):
servers = Role_Servers.insert({Role_Servers.server_id: server_id, Role_Servers.role_id: role_id, Role_Servers.permissions: rs_permissions}).execute()
return servers
@staticmethod
def user_role_query(user_id):
user_query = User_Roles.select().where(User_Roles.user_id == user_id)
query = Roles.select().where(Roles.role_id == -1)
for u in user_query:
query = Roles.select().where(Roles.role_id == u.role_id)
return query
#************************************************************************************************
# Commands Methods
#************************************************************************************************
@staticmethod @staticmethod
def get_unactioned_commands(): def get_unactioned_commands():
query = Commands.select().where(Commands.executed == 0) query = Commands.select().where(Commands.executed == 0)
return db_helper.return_rows(query) return db_helper.return_rows(query)
@staticmethod
def get_server_friendly_name(server_id):
server_data = db_helper.get_server_data_by_id(server_id)
friendly_name = "{} with ID: {}".format(server_data.get('server_name', None), server_data.get('server_id', 0))
return friendly_name
@staticmethod @staticmethod
def send_command(user_id, server_id, remote_ip, command): def send_command(user_id, server_id, remote_ip, command):
@ -833,16 +906,6 @@ class db_shortcuts:
Commands.command: command Commands.command: command
}).execute() }).execute()
@staticmethod
def get_actity_log():
q = Audit_Log.select()
return db_helper.return_db_rows(q)
@staticmethod
def return_db_rows(model):
data = [model_to_dict(row) for row in model]
return data
@staticmethod @staticmethod
def mark_command_complete(command_id=None): def mark_command_complete(command_id=None):
if command_id is not None: if command_id is not None:
@ -851,6 +914,14 @@ class db_shortcuts:
Commands.executed: True Commands.executed: True
}).where(Commands.command_id == command_id).execute() }).where(Commands.command_id == command_id).execute()
#************************************************************************************************
# Audit_Log Methods
#************************************************************************************************
@staticmethod
def get_actity_log():
q = Audit_Log.select()
return db_helper.return_db_rows(q)
def add_to_audit_log(self, 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("Adding to audit log User:{} - Message: {} ".format(user_id, log_msg)) logger.debug("Adding to audit log User:{} - Message: {} ".format(user_id, log_msg))
user_data = self.get_user(user_id) user_data = self.get_user(user_id)
@ -877,6 +948,9 @@ class db_shortcuts:
Audit_Log.source_ip: source_ip Audit_Log.source_ip: source_ip
}).execute() }).execute()
#************************************************************************************************
# Schedules Methods
#************************************************************************************************
@staticmethod @staticmethod
def create_scheduled_task(server_id, action, interval, interval_type, start_time, command, comment=None, enabled=True): def create_scheduled_task(server_id, action, interval, interval_type, start_time, command, comment=None, enabled=True):
sch_id = Schedules.insert({ sch_id = Schedules.insert({
@ -916,6 +990,9 @@ class db_shortcuts:
def get_schedules_enabled(): def get_schedules_enabled():
return Schedules.select().where(Schedules.enabled == True).execute() return Schedules.select().where(Schedules.enabled == True).execute()
#************************************************************************************************
# Backups Methods
#************************************************************************************************
@staticmethod @staticmethod
def get_backup_config(server_id): def get_backup_config(server_id):
try: try:
@ -937,15 +1014,6 @@ class db_shortcuts:
} }
return conf return conf
@staticmethod
def set_update(server_id, value):
try:
row = Server_Stats.select().where(Server_Stats.server_id == server_id)
except Exception as ex:
logger.error("Database entry not found. ".format(ex))
with database.atomic():
Server_Stats.update(updating=value).where(Server_Stats.server_id == server_id).execute()
@staticmethod @staticmethod
def set_backup_config(server_id: int, backup_path: str = None, max_backups: int = None, auto_enabled: bool = True): def set_backup_config(server_id: int, backup_path: str = None, max_backups: int = None, auto_enabled: bool = True):
logger.debug("Updating server {} backup config with {}".format(server_id, locals())) logger.debug("Updating server {} backup config with {}".format(server_id, locals()))
@ -992,6 +1060,10 @@ class db_shortcuts:
b = Backups.create(**conf) b = Backups.create(**conf)
logger.debug("Creating new backup record.") logger.debug("Creating new backup record.")
#************************************************************************************************
# Servers Permissions Class
#************************************************************************************************
class Enum_Permissions_Server(Enum): class Enum_Permissions_Server(Enum):
Commands = 0 Commands = 0
Terminal = 1 Terminal = 1
@ -1037,6 +1109,9 @@ class Permissions_Servers:
def get_permission(permission_mask, permission_tested: Enum_Permissions_Server): def get_permission(permission_mask, permission_tested: Enum_Permissions_Server):
return permission_mask[permission_tested.value] return permission_mask[permission_tested.value]
#************************************************************************************************
# Crafty Permissions Class
#************************************************************************************************
class Enum_Permissions_Crafty(Enum): class Enum_Permissions_Crafty(Enum):
Server_Creation = 0 Server_Creation = 0
User_Config = 1 User_Config = 1
@ -1077,6 +1152,10 @@ class Permissions_Crafty:
def get_permission(permission_mask, permission_tested: Enum_Permissions_Crafty): def get_permission(permission_mask, permission_tested: Enum_Permissions_Crafty):
return permission_mask[permission_tested.value] return permission_mask[permission_tested.value]
#************************************************************************************************
# Static Accessors
#************************************************************************************************
installer = db_builder() installer = db_builder()
db_helper = db_shortcuts() db_helper = db_shortcuts()
server_permissions = Permissions_Servers() server_permissions = Permissions_Servers()

View File

@ -115,7 +115,6 @@ class Server:
self.settings = server_data_obj self.settings = server_data_obj
# build our server run command # build our server run command
self.setup_server_run_command()
if server_data_obj['auto_start']: if server_data_obj['auto_start']:
delay = int(self.settings['auto_start_delay']) delay = int(self.settings['auto_start_delay'])

View File

@ -0,0 +1,55 @@
from re import template
import sys
import json
import logging
import tornado.web
import tornado.escape
import requests
from app.classes.shared.helpers import helper
from app.classes.web.base_handler import BaseHandler
from app.classes.shared.console import console
from app.classes.shared.models import Users, fn, db_helper
logger = logging.getLogger(__name__)
try:
import bleach
except ModuleNotFoundError as e:
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
console.critical("Import Error: Unable to load {} module".format(e.name))
sys.exit(1)
class StatusHandler(BaseHandler):
def get(self):
page_data = {}
page_data['servers'] = db_helper.get_all_servers_stats()
for srv in page_data['servers']:
server_data = srv.get('server_data', False)
server_id = server_data.get('server_id', False)
srv['raw_ping_result'] = self.controller.stats.get_raw_server_stats(server_id)
template = 'public/status.html'
self.render(
template,
data=page_data,
translate=self.translator.translate,
)
def post(self):
page_data = {}
page_data['servers'] = db_helper.get_all_servers_stats()
for srv in page_data['servers']:
server_data = srv.get('server_data', False)
server_id = server_data.get('server_id', False)
srv['raw_ping_result'] = self.controller.stats.get_raw_server_stats(server_id)
template = 'public/status.html'
self.render(
template,
data=page_data,
translate=self.translator.translate,
)

View File

@ -29,6 +29,7 @@ try:
from app.classes.shared.translation import translation from app.classes.shared.translation import translation
from app.classes.web.upload_handler import UploadHandler from app.classes.web.upload_handler import UploadHandler
from app.classes.web.http_handler import HTTPHandler, HTTPHandlerPage from app.classes.web.http_handler import HTTPHandler, HTTPHandlerPage
from app.classes.web.status_handler import StatusHandler
except ModuleNotFoundError as e: except ModuleNotFoundError as e:
logger.critical("Import Error: Unable to load {} module".format(e, e.name)) logger.critical("Import Error: Unable to load {} module".format(e, e.name))
@ -132,6 +133,7 @@ class Webserver:
(r'/api/stats/node', NodeStats, handler_args), (r'/api/stats/node', NodeStats, handler_args),
(r'/ws', SocketHandler, handler_args), (r'/ws', SocketHandler, handler_args),
(r'/upload', UploadHandler), (r'/upload', UploadHandler),
(r'/status', StatusHandler, handler_args)
] ]
app = tornado.web.Application( app = tornado.web.Application(

View File

@ -80,3 +80,8 @@ body { background-color: var(--dark) !important; /* Firefox */ }
.actions_serverlist > a > i { .actions_serverlist > a > i {
cursor: pointer; cursor: pointer;
} }
.corner {
position: absolute;
margin-top: 0;
margin-left: 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,127 @@
var obfuscators = [];
var styleMap = {
'§0': 'color:#000000',
'§1': 'color:#0000AA',
'§2': 'color:#00AA00',
'§3': 'color:#00AAAA',
'§4': 'color:#AA0000',
'§5': 'color:#AA00AA',
'§6': 'color:#FFAA00',
'§7': 'color:#AAAAAA',
'§8': 'color:#555555',
'§9': 'color:#5555FF',
'§a': 'color:#55FF55',
'§b': 'color:#55FFFF',
'§c': 'color:#FF5555',
'§d': 'color:#FF55FF',
'§e': 'color:#FFFF55',
'§f': 'color:#FFFFFF',
'§l': 'font-weight:bold',
'§m': 'text-decoration:line-through',
'§n': 'text-decoration:underline',
'§o': 'font-style:italic',
};
function obfuscate(string, elem) {
var magicSpan,
currNode;
if(string.indexOf('<br>') > -1) {
elem.innerHTML = string;
for(var j = 0, len = elem.childNodes.length; j < len; j++) {
currNode = elem.childNodes[j];
if(currNode.nodeType === 3) {
magicSpan = document.createElement('span');
magicSpan.innerHTML = currNode.nodeValue;
elem.replaceChild(magicSpan, currNode);
init(magicSpan);
}
}
} else {
init(elem, string);
}
function init(el, str) {
var i = 0,
obsStr = str || el.innerHTML,
len = obsStr.length;
obfuscators.push( window.setInterval(function () {
if(i >= len) i = 0;
obsStr = replaceRand(obsStr, i);
el.innerHTML = obsStr;
i++;
}, 0) );
}
function randInt(min, max) {
return Math.floor( Math.random() * (max - min + 1) ) + min;
}
function replaceRand(string, i) {
var randChar = String.fromCharCode( randInt(64, 95) );
return string.substr(0, i) + randChar + string.substr(i + 1, string.length);
}
}
function applyCode(string, codes) {
var elem = document.createElement('span'),
obfuscated = false;
string = string.replace(/\x00*/g, '');
for(var i = 0, len = codes.length; i < len; i++) {
elem.style.cssText += styleMap[codes[i]] + ';';
if(codes[i] === '§k') {
obfuscate(string, elem);
obfuscated = true;
}
}
if(!obfuscated) elem.innerHTML = string;
return elem;
}
function parseStyle(string) {
var codes = string.match(/§.{1}/g) || [],
indexes = [],
apply = [],
tmpStr,
deltaIndex,
noCode,
final = document.createDocumentFragment(),
i;
string = string.replace(/\n|\\n/g, '<br>');
for(i = 0, len = codes.length; i < len; i++) {
indexes.push( string.indexOf(codes[i]) );
string = string.replace(codes[i], '\x00\x00');
}
if(indexes[0] !== 0) {
final.appendChild( applyCode( string.substring(0, indexes[0]), [] ) );
}
for(i = 0; i < len; i++) {
indexDelta = indexes[i + 1] - indexes[i];
if(indexDelta === 2) {
while(indexDelta === 2) {
apply.push ( codes[i] );
i++;
indexDelta = indexes[i + 1] - indexes[i];
}
apply.push ( codes[i] );
} else {
apply.push( codes[i] );
}
if( apply.lastIndexOf('§r') > -1) {
apply = apply.slice( apply.lastIndexOf('§r') + 1 );
}
tmpStr = string.substring( indexes[i], indexes[i + 1] );
final.appendChild( applyCode(tmpStr, apply) );
}
return final;
}
function clearObfuscators() {
var i = obfuscators.length;
for(;i--;) {
clearInterval(obfuscators[i]);
}
obfuscators = [];
}
function initParser(input, output) {
clearObfuscators();
var input = document.getElementById(input),
output = document.getElementById(output);
if (input != null && output != null) {
var parsed = parseStyle( input.innerHTML );
output.innerHTML = '';
output.appendChild(parsed);
}
}

View File

@ -11,6 +11,8 @@
<link rel="stylesheet" href="/static/assets/vendors/ti-icons/css/themify-icons.css"> <link rel="stylesheet" href="/static/assets/vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" href="/static/assets/vendors/typicons/typicons.css"> <link rel="stylesheet" href="/static/assets/vendors/typicons/typicons.css">
<link rel="stylesheet" href="/static/assets/vendors/css/vendor.bundle.base.css"> <link rel="stylesheet" href="/static/assets/vendors/css/vendor.bundle.base.css">
<link rel="stylesheet" href="/static/assets/vendors/fontawesome5/css/all.css">
<link rel="stylesheet" href="/static/assest/css/crafty.css">
<!-- endinject --> <!-- endinject -->
<!-- Plugin css for this page --> <!-- Plugin css for this page -->
<!-- End Plugin css for this page --> <!-- End Plugin css for this page -->
@ -24,7 +26,7 @@
<div class="container-fluid page-body-wrapper full-page-wrapper"> <div class="container-fluid page-body-wrapper full-page-wrapper">
<div class="content-wrapper d-flex align-items-center auth auth-bg-1 theme-one"> <div class="content-wrapper d-flex align-items-center auth auth-bg-1 theme-one">
<div class="row w-100"> <div class="row w-100">
<div class="col-lg-4 mx-auto"> <div class="mx-auto">
{% block content %} {% block content %}
{% end %} {% end %}
@ -47,5 +49,11 @@
<script src="/static/assets/js/shared/settings.js"></script> <script src="/static/assets/js/shared/settings.js"></script>
<script src="/static/assets/js/shared/todolist.js"></script> <script src="/static/assets/js/shared/todolist.js"></script>
<!-- endinject --> <!-- endinject -->
{% block js %}
<!-- Custom js for this page -->
<!-- End custom js for this page -->
{% end %}
</body> </body>
</html> </html>

View File

@ -184,7 +184,7 @@
</label> </label>
</div> </div>
{% end %} {% end %}
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{ translate('panelConfig', 'save') }}</button> <button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{ translate('panelConfig', 'save') }}</button>
<button type="reset" onclick="location.href='/panel/panel_config'" class="btn btn-light"><i class="fas fa-undo-alt"></i> {{ translate('panelConfig', 'cancel') }}</button> <button type="reset" onclick="location.href='/panel/panel_config'" class="btn btn-light"><i class="fas fa-undo-alt"></i> {{ translate('panelConfig', 'cancel') }}</button>
</form> </form>

View File

@ -0,0 +1,82 @@
{% extends ../public_base.html %}
{% block meta %}
<meta http-equiv="refresh" content="30">
{% end %}
{% block title %}Crafty Controller - {{ translate('dashboard', 'dashboard') }}{% end %}
{% block content %}
<div class="content-wrapper col-md login-modal" style="background-color: #222437;">
<img src="/static/assets/images/logo_long.png" style='width: 25%; margin-left: 38%;'>
<hr />
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr class="rounded">
<th>{{ translate('dashboard', 'server') }}</th>
<th>{{ translate('dashboard', 'players') }}</th>
<th>{{ translate('dashboard', 'motd') }}</th>
<th>{{ translate('dashboard', 'version') }}</th>
<th>{{ translate('dashboard', 'status') }}</th>
</tr>
</thead>
<tbody>
{% for server in data['servers'] %}
<tr>
<td>
<i class="fas fa-server"></i>
{{ server['server_data']['server_name'] }}
</td>
{% if server['stats']['int_ping_results'] != 'False' %}
<td>
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard', 'max') }}<br />
</td>
<td>
{% if server['stats']['desc'] != 'False' %}
{% if server['raw_ping_result']['icon'] %}
<img src="data:image/png;base64,{% raw server['raw_ping_result']['icon'] %}" alt="icon"/>
{% else %}
<img src="/static/assets/images/pack.png" alt="icon" />
{% end %}
<span id="input_motd">{{ server['stats']['desc'] }}</span> <br />
{% end %}
</td>
<td>
{% if server['stats']['version'] != 'False' %}
{{ server['stats']['version'] }}
{% end %}
</td>
{% else %}
<td colspan="3">
<span class="text-warning"><i class="fas fa-exclamation-triangle"></i> Crafty can't get infos from this Server </span>
</td>
{% end %}
<td>
{% if server['stats']['int_ping_results'] %}
<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online') }}</span>
{% else %}
<span class="text-danger"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline') }}</span>
{% end %}
</td>
</tr>
{% end %}
</tbody>
</table>
</div>
<hr />
</div>
{% end %}
{% block js %}
<script src="/static/assets/js/motd.js"></script>
<script>
$(document).ready(function () {
initParser('input_motd', 'input_motd');
}());
</script>
{% end %}

View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{% block meta %}{% end %}
<title>{% block title %}{{ _('Default') }}{% end %}</title>
<!-- plugins:css -->
<link rel="stylesheet" href="/static/assets/vendors/mdi/css/materialdesignicons.min.css">
<link rel="stylesheet" href="/static/assets/vendors/flag-icon-css/css/flag-icon.min.css">
<link rel="stylesheet" href="/static/assets/vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" href="/static/assets/vendors/typicons/typicons.css">
<link rel="stylesheet" href="/static/assets/vendors/css/vendor.bundle.base.css">
<link rel="stylesheet" href="/static/assets/vendors/fontawesome5/css/all.css">
<!-- endinject -->
<!-- Plugin css for this page -->
<!-- End Plugin css for this page -->
<!-- Layout styles -->
<link rel="stylesheet" href="/static/assets/css/dark/style.css">
<!-- End Layout styles -->
<link rel="shortcut icon" href="/static/assets/images/favicon.png" />
</head>
<body class="dark-theme">
<div class="container-scroller">
<div class="container-fluid page-body-wrapper full-page-wrapper">
<div class="content-wrapper d-flex align-items-center auth auth-bg-1 theme-one">
<div class="row w-100">
<div class="mx-auto">
<div class="auto-form-wrapper">
{% block content %}
{% end %}
</div>
</div>
</div>
</div>
<!-- content-wrapper ends -->
</div>
<!-- page-body-wrapper ends -->
</div>
<!-- container-scroller -->
<!-- plugins:js -->
<script src="/static/assets/vendors/js/vendor.bundle.base.js"></script>
<!-- endinject -->
<!-- inject:js -->
<script src="/static/assets/js/shared/off-canvas.js"></script>
<script src="/static/assets/js/shared/hoverable-collapse.js"></script>
<script src="/static/assets/js/shared/misc.js"></script>
<script src="/static/assets/js/shared/settings.js"></script>
<script src="/static/assets/js/shared/todolist.js"></script>
<!-- endinject -->
{% block js %}
<!-- Custom js for this page -->
<!-- End custom js for this page -->
{% end %}
</body>
</html>

View File

@ -70,6 +70,8 @@
"server": "Server", "server": "Server",
"actions": "Actions", "actions": "Actions",
"world": "World", "world": "World",
"motd": "MOTD",
"version": "Version",
"status": "Status", "status": "Status",
"online": "Online", "online": "Online",
"offline": "Offline", "offline": "Offline",

View File

@ -70,6 +70,8 @@
"server": "Palvelin", "server": "Palvelin",
"actions": "Toiminnot", "actions": "Toiminnot",
"world": "Maailma", "world": "Maailma",
"motd": "MOTD",
"version": "Versio",
"status": "Tila", "status": "Tila",
"online": "Päällä", "online": "Päällä",
"offline": "Pois päältä", "offline": "Pois päältä",

View File

@ -70,6 +70,8 @@
"server": "Serveur", "server": "Serveur",
"actions": "Actions", "actions": "Actions",
"world": "Monde", "world": "Monde",
"motd": "MOTD",
"version": "Version",
"status": "Statut", "status": "Statut",
"online": "En Ligne", "online": "En Ligne",
"offline": "Hors Ligne", "offline": "Hors Ligne",