Merge branch 'dev-Silversthorn' of gitlab.com:crafty-controller/crafty-commander into dev-Silversthorn

This commit is contained in:
Silversthorn 2021-08-30 21:31:20 +02:00
commit b7802fdc45
20 changed files with 548 additions and 44 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,8 +28,26 @@ 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():
lines.append(e['text']) if e['text'] == '\n':
lines.append("§§")
else:
lines.append(e['text'])
total_text = " ".join(lines) total_text = " ".join(lines)
self.description = total_text self.description = total_text
@ -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))
@ -246,6 +254,62 @@ class Stats:
server_stats_list.append(server_stats) server_stats_list.append(server_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()

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):
@ -104,6 +112,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():
@ -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

@ -321,7 +321,7 @@ class db_shortcuts:
def get_all_defined_servers(): def get_all_defined_servers():
query = Servers.select() query = Servers.select()
return db_helper.return_rows(query) return db_helper.return_rows(query)
@staticmethod @staticmethod
def get_authorized_servers(user_id): def get_authorized_servers(user_id):
server_data = [] server_data = []
@ -408,7 +408,7 @@ class db_shortcuts:
for u in user_roles: for u in user_roles:
roles_list.append(db_helper.get_role(u.role_id)) roles_list.append(db_helper.get_role(u.role_id))
for r in roles_list: for r in roles_list:
role_test = Role_Servers.select().where(Role_Servers.role_id == r.get('role_id')) role_test = Role_Servers.select().where(Role_Servers.role_id == r.get('role_id'))
for t in role_test: for t in role_test:
@ -436,7 +436,7 @@ class db_shortcuts:
if not db_helper.get_server_data_by_id(server_id): if not db_helper.get_server_data_by_id(server_id):
return False return False
return True return True
@staticmethod @staticmethod
def server_id_authorized(serverId, user_id): def server_id_authorized(serverId, user_id):
authorized = 0 authorized = 0
@ -449,7 +449,7 @@ class db_shortcuts:
if authorized.count() == 0: if authorized.count() == 0:
return False return False
return True return True
@staticmethod @staticmethod
def set_update(server_id, value): def set_update(server_id, value):
try: try:
@ -485,16 +485,16 @@ class db_shortcuts:
@staticmethod @staticmethod
def get_crafty_permissions_list(user_id): def get_crafty_permissions_list(user_id):
permissions_mask = db_helper.get_crafty_permissions_mask(user_id) permissions_mask = db_helper.get_crafty_permissions_mask(user_id)
permissions_list = crafty_permissions.get_permissions(permissions_mask) permissions_list = crafty_permissions.get_permissions(permissions_mask)
return permissions_list return permissions_list
@staticmethod @staticmethod
def get_all_permission_quantity_list(): def get_all_permission_quantity_list():
quantity_list = { quantity_list = {
Enum_Permissions_Crafty.Server_Creation.name: -1, Enum_Permissions_Crafty.Server_Creation.name: -1,
Enum_Permissions_Crafty.User_Config.name: -1, Enum_Permissions_Crafty.User_Config.name: -1,
Enum_Permissions_Crafty.Roles_Config.name: -1, Enum_Permissions_Crafty.Roles_Config.name: -1,
} }
return quantity_list return quantity_list
@ -504,7 +504,7 @@ class db_shortcuts:
quantity_list = { quantity_list = {
Enum_Permissions_Crafty.Server_Creation.name: user_crafty.limit_server_creation, Enum_Permissions_Crafty.Server_Creation.name: user_crafty.limit_server_creation,
Enum_Permissions_Crafty.User_Config.name: user_crafty.limit_user_creation, Enum_Permissions_Crafty.User_Config.name: user_crafty.limit_user_creation,
Enum_Permissions_Crafty.Roles_Config.name: user_crafty.limit_role_creation, Enum_Permissions_Crafty.Roles_Config.name: user_crafty.limit_role_creation,
} }
return quantity_list return quantity_list
@ -516,7 +516,7 @@ class db_shortcuts:
try: try:
user_crafty = User_Crafty.select().where(User_Crafty.user_id == user_id).get() user_crafty = User_Crafty.select().where(User_Crafty.user_id == user_id).get()
except User_Crafty.DoesNotExist: except User_Crafty.DoesNotExist:
user_crafty = User_Crafty.Insert({ user_crafty = User_Crafty.insert({
User_Crafty.user_id: user_id, User_Crafty.user_id: user_id,
User_Crafty.permissions: "000", User_Crafty.permissions: "000",
User_Crafty.limit_server_creation: 0, User_Crafty.limit_server_creation: 0,
@ -540,16 +540,16 @@ class db_shortcuts:
quantity_list = { quantity_list = {
Enum_Permissions_Crafty.Server_Creation.name: user_crafty.created_server, Enum_Permissions_Crafty.Server_Creation.name: user_crafty.created_server,
Enum_Permissions_Crafty.User_Config.name: user_crafty.created_user, Enum_Permissions_Crafty.User_Config.name: user_crafty.created_user,
Enum_Permissions_Crafty.Roles_Config.name: user_crafty.created_role, Enum_Permissions_Crafty.Roles_Config.name: user_crafty.created_role,
} }
return quantity_list return quantity_list
@staticmethod @staticmethod
def get_crafty_limit_value(user_id, permission): def get_crafty_limit_value(user_id, permission):
user_crafty = db_helper.get_User_Crafty(user_id) user_crafty = db_helper.get_User_Crafty(user_id)
quantity_list = get_permission_quantity_list(user_id) quantity_list = get_permission_quantity_list(user_id)
return quantity_list[permission] return quantity_list[permission]
@staticmethod @staticmethod
def can_add_in_crafty(user_id, permission): def can_add_in_crafty(user_id, permission):
user_crafty = db_helper.get_User_Crafty(user_id) user_crafty = db_helper.get_User_Crafty(user_id)
@ -559,16 +559,20 @@ class db_shortcuts:
return can and ((quantity_list[permission.name] < limit_list[permission.name]) or limit_list[permission.name] == -1 ) return can and ((quantity_list[permission.name] < limit_list[permission.name]) or limit_list[permission.name] == -1 )
@staticmethod @staticmethod
def add_server_creation(user_id): def add_server_creation(user_id):
user_crafty = db_helper.get_User_Crafty(user_id) user_crafty = db_helper.get_User_Crafty(user_id)
user_crafty.created_server += 1 user_crafty.created_server += 1
User_Crafty.save(user_crafty) User_Crafty.save(user_crafty)
return user_crafty.created_server return user_crafty.created_server
<<<<<<< HEAD
#************************************************************************************************ #************************************************************************************************
# Host_Stats Methods # Host_Stats Methods
#************************************************************************************************ #************************************************************************************************
=======
>>>>>>> 45739a2e5ffd23d2373c51a4e5122e73ea174359
@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()
@ -611,7 +615,48 @@ class db_shortcuts:
return user return user
else: else:
return {} return {}
<<<<<<< HEAD
=======
@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
>>>>>>> 45739a2e5ffd23d2373c51a4e5122e73ea174359
@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)
@ -937,7 +982,7 @@ class db_shortcuts:
Audit_Log.log_msg: audit_msg, Audit_Log.log_msg: audit_msg,
Audit_Log.source_ip: source_ip Audit_Log.source_ip: source_ip
}).execute() }).execute()
@staticmethod @staticmethod
def add_to_audit_log_raw(user_name, user_id, server_id, log_msg, source_ip): def add_to_audit_log_raw(user_name, user_id, server_id, log_msg, source_ip):
Audit_Log.insert({ Audit_Log.insert({
@ -1082,7 +1127,7 @@ class Permissions_Servers:
for member in Enum_Permissions_Server.__members__.items(): for member in Enum_Permissions_Server.__members__.items():
permissions_list.append(member[1]) permissions_list.append(member[1])
return permissions_list return permissions_list
@staticmethod @staticmethod
def get_permissions(permissions_mask): def get_permissions(permissions_mask):
permissions_list = [] permissions_list = []
@ -1097,17 +1142,17 @@ class Permissions_Servers:
if permission_mask[permission_tested.value] == '1': if permission_mask[permission_tested.value] == '1':
result = True result = True
return result return result
@staticmethod @staticmethod
def set_permission(permission_mask, permission_tested: Enum_Permissions_Server, value): def set_permission(permission_mask, permission_tested: Enum_Permissions_Server, value):
l = list(permission_mask) l = list(permission_mask)
l[permission_tested.value] = str(value) l[permission_tested.value] = str(value)
permission_mask = ''.join(l) permission_mask = ''.join(l)
return permission_mask return permission_mask
@staticmethod @staticmethod
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 # Crafty Permissions Class
@ -1116,7 +1161,7 @@ class Enum_Permissions_Crafty(Enum):
Server_Creation = 0 Server_Creation = 0
User_Config = 1 User_Config = 1
Roles_Config = 2 Roles_Config = 2
class Permissions_Crafty: class Permissions_Crafty:
@staticmethod @staticmethod
@ -1125,7 +1170,7 @@ class Permissions_Crafty:
for member in Enum_Permissions_Crafty.__members__.items(): for member in Enum_Permissions_Crafty.__members__.items():
permissions_list.append(member[1]) permissions_list.append(member[1])
return permissions_list return permissions_list
@staticmethod @staticmethod
def get_permissions(permissions_mask): def get_permissions(permissions_mask):
permissions_list = [] permissions_list = []
@ -1140,17 +1185,17 @@ class Permissions_Crafty:
if permission_mask[permission_tested.value] == '1': if permission_mask[permission_tested.value] == '1':
result = True result = True
return result return result
@staticmethod @staticmethod
def set_permission(permission_mask, permission_tested: Enum_Permissions_Crafty, value): def set_permission(permission_mask, permission_tested: Enum_Permissions_Crafty, value):
l = list(permission_mask) l = list(permission_mask)
l[permission_tested.value] = str(value) l[permission_tested.value] = str(value)
permission_mask = ''.join(l) permission_mask = ''.join(l)
return permission_mask return permission_mask
@staticmethod @staticmethod
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]
#************************************************************************************************ #************************************************************************************************
@ -1159,4 +1204,4 @@ class Permissions_Crafty:
installer = db_builder() installer = db_builder()
db_helper = db_shortcuts() db_helper = db_shortcuts()
server_permissions = Permissions_Servers() server_permissions = Permissions_Servers()
crafty_permissions = Permissions_Crafty() crafty_permissions = Permissions_Crafty()

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",