Merge branch 'backups-and-stuff' into dev

This commit is contained in:
computergeek125 2021-03-21 23:32:48 -05:00
commit 506c9bb3c3
31 changed files with 1267 additions and 623 deletions

1
.gitignore vendored
View File

@ -18,6 +18,7 @@ venv.bak/
.idea/ .idea/
servers/ servers/
backups/
session.lock session.lock
.header .header
default.json default.json

View File

@ -9,7 +9,6 @@ from datetime import datetime
from app.classes.shared.helpers import helper 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.shared.controller import controller
from app.classes.minecraft.server_props import ServerProps from app.classes.minecraft.server_props import ServerProps
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -8,7 +8,6 @@ import datetime
from app.classes.shared.helpers import helper from app.classes.shared.helpers import helper
from app.classes.minecraft.mc_ping import ping from app.classes.minecraft.mc_ping import ping
from app.classes.shared.controller import controller
from app.classes.shared.models import db_helper from app.classes.shared.models import db_helper
from app.classes.shared.models import Host_Stats, Server_Stats from app.classes.shared.models import Host_Stats, Server_Stats
@ -17,6 +16,9 @@ logger = logging.getLogger(__name__)
class Stats: class Stats:
def __init__(self, controller):
self.controller = controller
def get_node_stats(self): def get_node_stats(self):
boot_time = datetime.datetime.fromtimestamp(psutil.boot_time()) boot_time = datetime.datetime.fromtimestamp(psutil.boot_time())
data = {} data = {}
@ -184,7 +186,7 @@ class Stats:
server_stats_list = [] server_stats_list = []
server_stats = {} server_stats = {}
servers = controller.servers_list servers = self.controller.servers_list
logger.info("Getting Stats for all servers...") logger.info("Getting Stats for all servers...")
@ -211,7 +213,7 @@ class Stats:
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 server '{}' on {}:{}".format(s.get('server_name', "ID#{}".format(server_id)), internal_ip, server_port))
int_mc_ping = ping(internal_ip, int(server_port)) int_mc_ping = ping(internal_ip, int(server_port))
int_data = False int_data = False
@ -289,5 +291,3 @@ class Stats:
Host_Stats.delete().where(Host_Stats.time < last_week).execute() Host_Stats.delete().where(Host_Stats.time < last_week).execute()
Server_Stats.delete().where(Server_Stats.created < last_week).execute() Server_Stats.delete().where(Server_Stats.created < last_week).execute()
stats = Stats()

View File

@ -20,7 +20,11 @@ except ModuleNotFoundError as e:
sys.exit(1) sys.exit(1)
class MainPrompt(cmd.Cmd): class MainPrompt(cmd.Cmd, object):
def __init__(self, tasks_manager):
super().__init__()
self.tasks_manager = tasks_manager
# overrides the default Prompt # overrides the default Prompt
prompt = "Crafty Controller v{} > ".format(helper.get_version_string()) prompt = "Crafty Controller v{} > ".format(helper.get_version_string())

View File

@ -15,6 +15,7 @@ from app.classes.shared.models import db_helper, Servers, User_Servers
from app.classes.shared.server import Server from app.classes.shared.server import Server
from app.classes.minecraft.server_props import ServerProps from app.classes.minecraft.server_props import ServerProps
from app.classes.minecraft.serverjars import server_jar_obj from app.classes.minecraft.serverjars import server_jar_obj
from app.classes.minecraft.stats import Stats
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -23,6 +24,7 @@ class Controller:
def __init__(self): def __init__(self):
self.servers_list = [] self.servers_list = []
self.stats = Stats(self)
def check_server_loaded(self, server_id_to_check: int): def check_server_loaded(self, server_id_to_check: int):
@ -72,7 +74,7 @@ class Controller:
temp_server_dict = { temp_server_dict = {
'server_id': s.get('server_id'), 'server_id': s.get('server_id'),
'server_data_obj': s, 'server_data_obj': s,
'server_obj': Server(), 'server_obj': Server(self.stats),
'server_settings': settings.props 'server_settings': settings.props
} }
@ -94,7 +96,6 @@ class Controller:
server_obj.reload_server_settings() server_obj.reload_server_settings()
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):
return s['server_obj'] return s['server_obj']
@ -196,12 +197,14 @@ class Controller:
def create_jar_server(self, server: str, version: str, name: str, min_mem: int, max_mem: int, port: int): def create_jar_server(self, server: str, version: str, name: str, min_mem: int, max_mem: int, port: int):
server_id = helper.create_uuid() server_id = helper.create_uuid()
server_dir = os.path.join(helper.servers_dir, server_id) server_dir = os.path.join(helper.servers_dir, server_id)
backup_path = os.path.join(helper.backup_path, server_id)
server_file = "{server}-{version}.jar".format(server=server, version=version) server_file = "{server}-{version}.jar".format(server=server, version=version)
full_jar_path = os.path.join(server_dir, server_file) full_jar_path = os.path.join(server_dir, server_file)
# make the dir - perhaps a UUID? # make the dir - perhaps a UUID?
helper.ensure_dir_exists(server_dir) helper.ensure_dir_exists(server_dir)
helper.ensure_dir_exists(backup_path)
try: try:
# do a eula.txt # do a eula.txt
@ -227,7 +230,7 @@ class Controller:
# 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)
new_id = self.register_server(name, server_id, server_dir, 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
@staticmethod @staticmethod
@ -248,8 +251,10 @@ class Controller:
def import_jar_server(self, server_name: str, server_path: str, server_jar: str, min_mem: int, max_mem: int, port: int): def import_jar_server(self, server_name: str, server_path: str, server_jar: str, min_mem: int, max_mem: int, port: int):
server_id = helper.create_uuid() server_id = helper.create_uuid()
new_server_dir = os.path.join(helper.servers_dir, server_id) new_server_dir = os.path.join(helper.servers_dir, server_id)
backup_path = os.path.join(helper.backup_path, server_id)
helper.ensure_dir_exists(new_server_dir) helper.ensure_dir_exists(new_server_dir)
helper.ensure_dir_exists(backup_path)
dir_util.copy_tree(server_path, new_server_dir) dir_util.copy_tree(server_path, new_server_dir)
full_jar_path = os.path.join(new_server_dir, server_jar) full_jar_path = os.path.join(new_server_dir, server_jar)
@ -259,15 +264,18 @@ class Controller:
server_log_file = "{}/logs/latest.log".format(new_server_dir) server_log_file = "{}/logs/latest.log".format(new_server_dir)
server_stop = "stop" server_stop = "stop"
new_id = self.register_server(server_name, server_id, new_server_dir, server_command, server_jar, new_id = self.register_server(server_name, server_id, new_server_dir, backup_path, server_command, server_jar,
server_log_file, server_stop, port) server_log_file, server_stop, port)
return new_id return new_id
def import_zip_server(self, server_name: str, zip_path: str, server_jar: str, min_mem: int, max_mem: int, port: int): def import_zip_server(self, server_name: str, zip_path: str, server_jar: str, min_mem: int, max_mem: int, port: int):
server_id = helper.create_uuid() server_id = helper.create_uuid()
new_server_dir = os.path.join(helper.servers_dir, server_id) new_server_dir = os.path.join(helper.servers_dir, server_id)
backup_path = os.path.join(helper.backup_path, server_id)
if helper.check_file_perms(zip_path): if helper.check_file_perms(zip_path):
helper.ensure_dir_exists(new_server_dir) helper.ensure_dir_exists(new_server_dir)
helper.ensure_dir_exists(backup_path)
with zipfile.ZipFile(zip_path, 'r') as zip_ref: with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(new_server_dir) zip_ref.extractall(new_server_dir)
else: else:
@ -281,11 +289,11 @@ class Controller:
server_log_file = "{}/logs/latest.log".format(new_server_dir) server_log_file = "{}/logs/latest.log".format(new_server_dir)
server_stop = "stop" server_stop = "stop"
new_id = self.register_server(server_name, server_id, new_server_dir, server_command, server_jar, new_id = self.register_server(server_name, server_id, new_server_dir, backup_path, server_command, server_jar,
server_log_file, server_stop, port) server_log_file, server_stop, port)
return new_id return new_id
def register_server(self, name: str, server_id: str, server_dir: str, server_command: str, server_file: str, server_log_file: str, server_stop: str, server_port=25565): def register_server(self, name: str, server_id: str, server_dir: str, backup_path: str, server_command: str, server_file: str, server_log_file: str, server_stop: str, server_port=25565):
# put data in the db # put data in the db
new_id = Servers.insert({ new_id = Servers.insert({
Servers.server_name: name, Servers.server_name: name,
@ -298,7 +306,8 @@ class Controller:
Servers.crash_detection: False, Servers.crash_detection: False,
Servers.log_path: server_log_file, Servers.log_path: server_log_file,
Servers.server_port: server_port, Servers.server_port: server_port,
Servers.stop_command: server_stop Servers.stop_command: server_stop,
Servers.backup_path: backup_path
}).execute() }).execute()
try: try:
@ -343,5 +352,3 @@ class Controller:
self.servers_list.pop(counter) self.servers_list.pop(counter)
counter += 1 counter += 1
controller = Controller()

View File

@ -0,0 +1,8 @@
class CraftyException(Exception):
pass
class DatabaseException(CraftyException):
pass
class SchemaError(DatabaseException):
pass

View File

@ -10,6 +10,8 @@ import socket
import random import random
import logging import logging
import html import html
import zipfile
import pathlib
from datetime import datetime from datetime import datetime
from socket import gethostname from socket import gethostname
@ -36,6 +38,7 @@ class Helpers:
self.config_dir = os.path.join(self.root_dir, 'app', 'config') self.config_dir = os.path.join(self.root_dir, 'app', 'config')
self.webroot = os.path.join(self.root_dir, 'app', 'frontend') self.webroot = os.path.join(self.root_dir, 'app', 'frontend')
self.servers_dir = os.path.join(self.root_dir, 'servers') self.servers_dir = os.path.join(self.root_dir, 'servers')
self.backup_path = os.path.join(self.root_dir, 'backups')
self.session_file = os.path.join(self.root_dir, 'app', 'config', 'session.lock') self.session_file = os.path.join(self.root_dir, 'app', 'config', 'session.lock')
self.settings_file = os.path.join(self.root_dir, 'app', 'config', 'config.json') self.settings_file = os.path.join(self.root_dir, 'app', 'config', 'config.json')
@ -43,6 +46,7 @@ class Helpers:
self.ensure_dir_exists(os.path.join(self.root_dir, 'app', 'config', 'db')) self.ensure_dir_exists(os.path.join(self.root_dir, 'app', 'config', 'db'))
self.db_path = os.path.join(self.root_dir, 'app', 'config', 'db', 'crafty.sqlite') self.db_path = os.path.join(self.root_dir, 'app', 'config', 'db', 'crafty.sqlite')
self.serverjar_cache = os.path.join(self.config_dir, 'serverjars.json') self.serverjar_cache = os.path.join(self.config_dir, 'serverjars.json')
self.credits_cache = os.path.join(self.config_dir, 'credits.json')
self.passhasher = PasswordHasher() self.passhasher = PasswordHasher()
self.exiting = False self.exiting = False
@ -305,6 +309,8 @@ class Helpers:
@staticmethod @staticmethod
def check_path_exists(path: str): def check_path_exists(path: str):
if not path:
return False
logger.debug('Looking for path: {}'.format(path)) logger.debug('Looking for path: {}'.format(path))
if os.path.exists(path): if os.path.exists(path):
@ -371,6 +377,19 @@ class Helpers:
total += entry.stat(follow_symlinks=False).st_size total += entry.stat(follow_symlinks=False).st_size
return total return total
@staticmethod
def list_dir_by_date(path: str, reverse=False):
return [str(p) for p in sorted(pathlib.Path(path).iterdir(), key=os.path.getmtime, reverse=reverse)]
def get_human_readable_files_sizes(self, paths: list):
sizes = []
for p in paths:
sizes.append({
"path": p,
"size": self.human_readable_file_size(os.stat(p).st_size)
})
return sizes
@staticmethod @staticmethod
def base64_encode_string(string: str): def base64_encode_string(string: str):
s_bytes = str(string).encode('utf-8') s_bytes = str(string).encode('utf-8')
@ -520,7 +539,7 @@ class Helpers:
@staticmethod @staticmethod
def get_banned_players(server_id, db_helper): def get_banned_players(server_id, db_helper):
stats = db_helper.get_server_stats_by_id(server_id) stats = db_helper.get_server_stats_by_id(server_id)
server_path = stats[0]['server_id']['path'] server_path = stats['server_id']['path']
path = os.path.join(server_path, 'banned-players.json') path = os.path.join(server_path, 'banned-players.json')
try: try:
@ -533,5 +552,13 @@ class Helpers:
return json.loads(content) return json.loads(content)
@staticmethod
def zip_directory(file, path, compression=zipfile.ZIP_LZMA):
with zipfile.ZipFile(file, 'w', compression) as zf:
for root, dirs, files in os.walk(path):
for file in files:
zf.write(os.path.join(root, file),
os.path.relpath(os.path.join(root, file),
os.path.join(path, '..')))
helper = Helpers() helper = Helpers()

View File

@ -9,6 +9,8 @@ from app.classes.minecraft.server_props import ServerProps
from app.classes.web.websocket_helper import websocket_helper from app.classes.web.websocket_helper import websocket_helper
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
peewee_logger = logging.getLogger('peewee')
peewee_logger.setLevel(logging.INFO)
try: try:
from peewee import * from peewee import *
@ -20,15 +22,30 @@ except ModuleNotFoundError as e:
console.critical("Import Error: Unable to load {} module".format(e, e.name)) console.critical("Import Error: Unable to load {} module".format(e, e.name))
sys.exit(1) sys.exit(1)
schema_version = (0, 1, 0) # major, minor, patch semver
database = SqliteDatabase(helper.db_path, pragmas={ database = SqliteDatabase(helper.db_path, pragmas={
'journal_mode': 'wal', 'journal_mode': 'wal',
'cache_size': -1024 * 10}) 'cache_size': -1024 * 10})
class BaseModel(Model): class BaseModel(Model):
class Meta: class Meta:
database = database database = database
class SchemaVersion(BaseModel):
# DO NOT EVER CHANGE THE SCHEMA OF THIS TABLE
# (unless we have a REALLY good reason to)
# There will only ever be one row, and it allows the database loader to detect
# what it needs to do on major version upgrades so you don't have to wipe the DB
# every time you upgrade
schema_major = IntegerField()
schema_minor = IntegerField()
schema_patch = IntegerField()
class Meta:
table_name = 'schema_version'
primary_key = CompositeKey('schema_major', 'schema_minor', 'schema_patch')
class Users(BaseModel): class Users(BaseModel):
user_id = AutoField() user_id = AutoField()
@ -97,6 +114,7 @@ class Servers(BaseModel):
server_uuid = CharField(default="", index=True) server_uuid = CharField(default="", index=True)
server_name = CharField(default="Server", index=True) server_name = CharField(default="Server", index=True)
path = CharField(default="") path = CharField(default="")
backup_path = CharField(default="")
executable = CharField(default="") executable = CharField(default="")
log_path = CharField(default="") log_path = CharField(default="")
execution_command = CharField(default="") execution_command = CharField(default="")
@ -111,16 +129,6 @@ class Servers(BaseModel):
class Meta: class Meta:
table_name = "servers" table_name = "servers"
class User_Servers(BaseModel):
user_id = ForeignKeyField(Users, backref='user_server')
server_id = ForeignKeyField(Servers, backref='user_server')
class Meta:
table_name = 'user_servers'
primary_key = CompositeKey('user_id', 'server_id')
class Role_Servers(BaseModel): class Role_Servers(BaseModel):
role_id = ForeignKeyField(Roles, backref='role_server') role_id = ForeignKeyField(Roles, backref='role_server')
server_id = ForeignKeyField(Servers, backref='role_server') server_id = ForeignKeyField(Servers, backref='role_server')
@ -178,12 +186,25 @@ class Webhooks(BaseModel):
class Meta: class Meta:
table_name = "webhooks" table_name = "webhooks"
class Schedules(BaseModel):
schedule_id = IntegerField(unique=True, primary_key=True)
server_id = ForeignKeyField(Servers, backref='schedule_server')
enabled = BooleanField()
action = CharField()
interval = IntegerField()
interval_type = CharField()
start_time = CharField(null=True)
command = CharField(null=True)
comment = CharField()
class Meta:
table_name = 'schedules'
class Backups(BaseModel): class Backups(BaseModel):
directories = CharField() directories = CharField(null=True)
storage_location = CharField()
max_backups = IntegerField() max_backups = IntegerField()
server_id = IntegerField(index=True) server_id = ForeignKeyField(Servers, backref='backups_server')
schedule_id = ForeignKeyField(Schedules, backref='backups_schedule')
class Meta: class Meta:
table_name = 'backups' table_name = 'backups'
@ -202,17 +223,23 @@ class db_builder:
Host_Stats, Host_Stats,
Webhooks, Webhooks,
Servers, Servers,
User_Servers,
Role_Servers, Role_Servers,
Server_Stats, Server_Stats,
Commands, Commands,
Audit_Log Audit_Log,
SchemaVersion,
Schedules
]) ])
@staticmethod @staticmethod
def default_settings(): def default_settings():
logger.info("Fresh Install Detected - Creating Default Settings") logger.info("Fresh Install Detected - Creating Default Settings")
console.info("Fresh Install Detected - Creating Default Settings") console.info("Fresh Install Detected - Creating Default Settings")
SchemaVersion.insert({
SchemaVersion.schema_major: schema_version[0],
SchemaVersion.schema_minor: schema_version[1],
SchemaVersion.schema_patch: schema_version[2]
}).execute()
default_data = helper.find_default_password() default_data = helper.find_default_password()
username = default_data.get("username", 'admin') username = default_data.get("username", 'admin')
@ -239,9 +266,39 @@ class db_builder:
return True return True
pass pass
@staticmethod
def check_schema_version():
svs = SchemaVersion.select().execute()
if len(svs) != 1:
raise exceptions.SchemaError("Multiple or no schema versions detected - potentially a failed upgrade?")
sv = svs[0]
svt = (sv.schema_major, sv.schema_minor, sv.schema_patch)
logger.debug("Schema: found {}, expected {}".format(svt, schema_version))
console.debug("Schema: found {}, expected {}".format(svt, schema_version))
if sv.schema_major > schema_version[0]:
raise exceptions.SchemaError("Major version mismatch - possible code reversion")
elif sv.schema_major < schema_version[0]:
db_shortcuts.upgrade_schema()
if sv.schema_minor > schema_version[1]:
logger.warning("Schema minor mismatch detected: found {}, expected {}. Proceed with caution".format(svt, schema_version))
console.warning("Schema minor mismatch detected: found {}, expected {}. Proceed with caution".format(svt, schema_version))
elif sv.schema_minor < schema_version[1]:
db_shortcuts.upgrade_schema()
if sv.schema_patch > schema_version[2]:
logger.info("Schema patch mismatch detected: found {}, expected {}. Proceed with caution".format(svt, schema_version))
console.info("Schema patch mismatch detected: found {}, expected {}. Proceed with caution".format(svt, schema_version))
elif sv.schema_patch < schema_version[2]:
db_shortcuts.upgrade_schema()
logger.info("Schema validation successful! {}".format(schema_version))
class db_shortcuts: class db_shortcuts:
@staticmethod
def upgrade_schema():
raise NotImplemented("I don't know who you are or how you reached this code, but this should NOT have happened. Please report it to the developer with due haste.")
@staticmethod @staticmethod
def return_rows(query): def return_rows(query):
rows = [] rows = []
@ -258,13 +315,12 @@ class db_shortcuts:
@staticmethod @staticmethod
def get_server_data_by_id(server_id): def get_server_data_by_id(server_id):
query = Servers.select().where(Servers.server_id == server_id).limit(1)
try: try:
query = Servers.get_by_id(server_id) return db_helper.return_rows(query)[0]
except DoesNotExist: except IndexError:
return {} return {}
return model_to_dict(query)
@staticmethod @staticmethod
def get_all_defined_servers(): def get_all_defined_servers():
query = Servers.select() query = Servers.select()
@ -307,7 +363,7 @@ class db_shortcuts:
for s in servers: 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) 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)}) server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)[0]})
return server_data return server_data
@staticmethod @staticmethod
@ -352,7 +408,7 @@ class db_shortcuts:
@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)
return db_helper.return_rows(stats) return db_helper.return_rows(stats)[0]
@staticmethod @staticmethod
def server_id_exists(server_id): def server_id_exists(server_id):
@ -417,6 +473,8 @@ class db_shortcuts:
@staticmethod @staticmethod
def get_userid_by_name(username): def get_userid_by_name(username):
if username == "SYSTEM":
return 0
try: try:
return (Users.get(Users.username == username)).user_id return (Users.get(Users.username == username)).user_id
except DoesNotExist: except DoesNotExist:
@ -424,6 +482,21 @@ class db_shortcuts:
@staticmethod @staticmethod
def get_user(user_id): def get_user(user_id):
if user_id == 0:
return {
user_id: 0,
created: None,
last_login: None,
last_update: None,
last_ip: "127.27.23.89",
username: "SYSTEM",
password: None,
enabled: True,
superuser: False,
api_token: None,
roles: [],
servers: []
}
user = model_to_dict(Users.get(Users.user_id == user_id)) user = model_to_dict(Users.get(Users.user_id == user_id))
if user: if user:
@ -432,13 +505,13 @@ class db_shortcuts:
roles = set() roles = set()
for r in roles_query: for r in roles_query:
roles.add(r.role_id.role_id) roles.add(r.role_id.role_id)
servers_query = User_Servers.select().join(Servers, JOIN.INNER).where(User_Servers.user_id == user_id) #servers_query = User_Servers.select().join(Servers, JOIN.INNER).where(User_Servers.user_id == user_id)
# TODO: this query needs to be narrower ## TODO: this query needs to be narrower
servers = set() servers = set()
for s in servers_query: #for s in servers_query:
servers.add(s.server_id.server_id) # servers.add(s.server_id.server_id)
user['roles'] = roles user['roles'] = roles
user['servers'] = servers #user['servers'] = servers
logger.debug("user: ({}) {}".format(user_id, user)) logger.debug("user: ({}) {}".format(user_id, user))
return user return user
else: else:
@ -478,10 +551,10 @@ class db_shortcuts:
# TODO: This is horribly inefficient and we should be using bulk queries but im going for functionality at this point # TODO: This is horribly inefficient and we should be using bulk queries but im going for functionality at this point
User_Roles.delete().where(User_Roles.user_id == user_id).where(User_Roles.role_id.in_(removed_roles)).execute() User_Roles.delete().where(User_Roles.user_id == user_id).where(User_Roles.role_id.in_(removed_roles)).execute()
for server in added_servers: #for server in added_servers:
User_Servers.get_or_create(user_id=user_id, server_id=server) # User_Servers.get_or_create(user_id=user_id, server_id=server)
# TODO: This is horribly inefficient and we should be using bulk queries but im going for functionality at this point # # TODO: This is horribly inefficient and we should be using bulk queries but im going for functionality at this point
User_Servers.delete().where(User_Servers.user_id == user_id).where(User_Servers.server_id.in_(removed_servers)).execute() #User_Servers.delete().where(User_Servers.user_id == user_id).where(User_Servers.server_id.in_(removed_servers)).execute()
if up_data: if up_data:
Users.update(up_data).where(Users.user_id == user_id).execute() Users.update(up_data).where(Users.user_id == user_id).execute()
@ -658,8 +731,111 @@ class db_shortcuts:
Audit_Log.source_ip: source_ip Audit_Log.source_ip: source_ip
}).execute() }).execute()
@staticmethod
def create_scheduled_task(server_id, action, interval, interval_type, start_time, command, comment=None, enabled=True):
sch_id = Schedules.insert({
Schedules.server_id: server_id,
Schedules.action: action,
Schedules.enabled: enabled,
Schedules.interval: interval,
Schedules.interval_type: interval_type,
Schedules.start_time: start_time,
Schedules.command: command,
Schedules.comment: comment
}).execute()
return sch_id
@staticmethod
def delete_scheduled_task(schedule_id):
sch = Schedules.get(Schedules.schedule_id == schedule_id)
return Schedules.delete_instance(sch)
@staticmethod
def update_scheduled_task(schedule_id, updates):
Schedules.update(updates).where(Schedules.schedule_id == schedule_id).execute()
@staticmethod
def get_scheduled_task(schedule_id):
return model_to_dict(Schedules.get(Schedules.schedule_id == schedule_id)).execute()
@staticmethod
def get_schedules_by_server(server_id):
return Schedules.select().where(Schedules.server_id == server_id).execute()
@staticmethod
def get_schedules_all():
return Schedules.select().execute()
@staticmethod
def get_schedules_enabled():
return Schedules.select().where(Schedules.enabled == True).execute()
@staticmethod
def get_backup_config(server_id):
try:
row = Backups.select().where(Backups.server_id == server_id).join(Schedules).join(Servers)[0]
conf = {
"backup_path": row.server_id.backup_path,
"directories": row.directories,
"max_backups": row.max_backups,
"auto_enabled": row.schedule_id.enabled,
"server_id": row.server_id.server_id
}
except IndexError:
conf = {
"backup_path": None,
"directories": None,
"max_backups": 0,
"auto_enabled": True,
"server_id": server_id
}
return conf
@staticmethod
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()))
try:
row = Backups.select().where(Backups.server_id == server_id).join(Schedules).join(Servers)[0]
new_row = False
conf = {}
schd = {}
except IndexError:
conf = {
"directories": None,
"max_backups": 0,
"server_id": server_id
}
schd = {
"enabled": True,
"action": "backup_server",
"interval_type": "days",
"interval": 1,
"start_time": "00:00",
"server_id": server_id,
"comment": "Default backup job"
}
new_row = True
if max_backups is not None:
conf['max_backups'] = max_backups
schd['enabled'] = bool(auto_enabled)
if not new_row:
with database.atomic():
if backup_path is not None:
u1 = Servers.update(backup_path=backup_path).where(Servers.server_id == server_id).execute()
else:
u1 = 0
u2 = Backups.update(conf).where(Backups.server_id == server_id).execute()
u3 = Schedules.update(schd).where(Schedules.schedule_id == row.schedule_id).execute()
logger.debug("Updating existing backup record. {}+{}+{} rows affected".format(u1, u2, u3))
else:
with database.atomic():
conf["server_id"] = server_id
if backup_path is not None:
u = Servers.update(backup_path=backup_path).where(Servers.server_id == server_id)
s = Schedules.create(**schd)
conf['schedule_id'] = s.schedule_id
b = Backups.create(**conf)
logger.debug("Creating new backup record.")
installer = db_builder() installer = db_builder()
db_helper = db_shortcuts() db_helper = db_shortcuts()

View File

@ -9,6 +9,7 @@ import datetime
import threading import threading
import schedule import schedule
import logging.config import logging.config
import zipfile
from app.classes.shared.helpers import helper from app.classes.shared.helpers import helper
@ -29,7 +30,7 @@ except ModuleNotFoundError as e:
class Server: class Server:
def __init__(self): def __init__(self, stats):
# holders for our process # holders for our process
self.process = None self.process = None
self.line = False self.line = False
@ -45,6 +46,7 @@ class Server:
self.is_crashed = False self.is_crashed = False
self.restart_count = 0 self.restart_count = 0
self.crash_watcher_schedule = None self.crash_watcher_schedule = None
self.stats = stats
def reload_server_settings(self): def reload_server_settings(self):
server_data = db_helper.get_server_data_by_id(self.server_id) server_data = db_helper.get_server_data_by_id(self.server_id)
@ -108,7 +110,6 @@ class Server:
helper.do_exit() helper.do_exit()
def start_server(self): def start_server(self):
from app.classes.minecraft.stats import stats
# fail safe in case we try to start something already running # fail safe in case we try to start something already running
if self.check_running(): if self.check_running():
@ -155,7 +156,6 @@ class Server:
self.server_thread.join() self.server_thread.join()
def stop_server(self): def stop_server(self):
from app.classes.minecraft.stats import stats
if self.settings['stop_command']: if self.settings['stop_command']:
self.send_command(self.settings['stop_command']) self.send_command(self.settings['stop_command'])
@ -189,7 +189,7 @@ class Server:
# massive resetting of variables # massive resetting of variables
self.cleanup_server_object() self.cleanup_server_object()
stats.record_stats() self.stats.record_stats()
def restart_threaded_server(self): def restart_threaded_server(self):
@ -318,3 +318,26 @@ class Server:
logger.info("Removing old crash detection watcher thread") logger.info("Removing old crash detection watcher thread")
console.info("Removing old crash detection watcher thread") console.info("Removing old crash detection watcher thread")
schedule.clear(self.name) schedule.clear(self.name)
def backup_server(self):
logger.info("Starting server {} (ID {}) backup".format(self.name, self.server_id))
conf = db_helper.get_backup_config(self.server_id)
try:
backup_filename = "{}/{}.zip".format(conf['backup_path'], datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'))
logger.info("Creating backup of server '{}' (ID#{}) at '{}'".format(self.settings['server_name'], self.server_id, backup_filename))
helper.zip_directory(backup_filename, self.server_path)
backup_list = self.list_backups()
if len(self.list_backups()) > conf["max_backups"]:
oldfile = backup_list[0]
logger.info("Removing old backup '{}'".format(oldfile))
os.remove(oldfile)
except:
logger.exception("Failed to create backup of server {} (ID {})".format(self.name, self.server_id))
def list_backups(self):
conf = db_helper.get_backup_config(self.server_id)
if helper.check_path_exists(self.settings['backup_path']):
files = helper.get_human_readable_files_sizes(helper.list_dir_by_date(self.settings['backup_path']))
return [{"path": os.path.relpath(f['path'], start=conf['backup_path']), "size": f["size"]} for f in files]
else:
return []

View File

@ -8,11 +8,9 @@ import asyncio
from app.classes.shared.helpers import helper from app.classes.shared.helpers import helper
from app.classes.shared.console import console from app.classes.shared.console import console
from app.classes.web.tornado import webserver from app.classes.web.tornado import Webserver
from app.classes.web.websocket_helper import websocket_helper from app.classes.web.websocket_helper import websocket_helper
from app.classes.minecraft.stats import stats
from app.classes.shared.controller import controller
from app.classes.minecraft.serverjars import server_jar_obj from app.classes.minecraft.serverjars import server_jar_obj
from app.classes.shared.models import db_helper from app.classes.shared.models import db_helper
@ -26,11 +24,26 @@ except ModuleNotFoundError as e:
console.critical("Import Error: Unable to load {} module".format(e, e.name)) console.critical("Import Error: Unable to load {} module".format(e, e.name))
sys.exit(1) sys.exit(1)
scheduler_intervals = { 'seconds',
'minutes',
'hours',
'days',
'weeks',
'monday',
'tuesday',
'wednesday',
'thursday',
'friday',
'saturday',
'sunday'
}
class TasksManager: class TasksManager:
def __init__(self): def __init__(self, controller):
self.tornado = webserver() self.controller = controller
self.tornado = Webserver(controller, self)
self.webserver_thread = threading.Thread(target=self.tornado.run_tornado, daemon=True, name='tornado_thread') self.webserver_thread = threading.Thread(target=self.tornado.run_tornado, daemon=True, name='tornado_thread')
self.main_kill_switch_thread = threading.Thread(target=self.main_kill_switch, daemon=True, name="main_loop") self.main_kill_switch_thread = threading.Thread(target=self.main_kill_switch, daemon=True, name="main_loop")
@ -39,13 +52,13 @@ class TasksManager:
self.schedule_thread = threading.Thread(target=self.scheduler_thread, daemon=True, name="scheduler") self.schedule_thread = threading.Thread(target=self.scheduler_thread, daemon=True, name="scheduler")
self.log_watcher_thread = threading.Thread(target=self.log_watcher, daemon=True, name="log_watcher") self.log_watcher_thread = threading.Thread(target=self.log_watcher, daemon=True, name="log_watcher")
self.log_watcher_thread.start()
self.command_thread = threading.Thread(target=self.command_watcher, daemon=True, name="command_watcher") self.command_thread = threading.Thread(target=self.command_watcher, daemon=True, name="command_watcher")
self.command_thread.start()
self.realtime_thread = threading.Thread(target=self.realtime, daemon=True, name="realtime") self.realtime_thread = threading.Thread(target=self.realtime, daemon=True, name="realtime")
self.realtime_thread.start()
self.reload_schedule_from_db()
def get_main_thread_run_status(self): def get_main_thread_run_status(self):
return self.main_thread_exiting return self.main_thread_exiting
@ -60,14 +73,29 @@ class TasksManager:
self._main_graceful_exit() self._main_graceful_exit()
time.sleep(5) time.sleep(5)
@staticmethod def reload_schedule_from_db(self):
def command_watcher(): jobs = db_helper.get_schedules_enabled()
schedule.clear(tag='backup')
schedule.clear(tag='db')
for j in jobs:
if j.interval_type in scheduler_intervals:
logger.info("Loading schedule ID#{i}: '{a}' every {n} {t} at {s}".format(
i=j.schedule_id, a=j.action, n=j.interval, t=j.interval_type, s=j.start_time))
try:
getattr(schedule.every(j.interval), j.interval_type).at(j.start_time).do(
db_helper.send_command, 0, j.server_id, "127.27.23.89", j.action)
except schedule.ScheduleValueError as e:
logger.critical("Scheduler value error occurred: {} on ID#{}".format(e, j.schedule_id))
else:
logger.critical("Unknown schedule job type '{}' at id {}, skipping".format(j.interval_type, j.schedule_id))
def command_watcher(self):
while True: while True:
# select any commands waiting to be processed # select any commands waiting to be processed
commands = db_helper.get_unactioned_commands() commands = db_helper.get_unactioned_commands()
for c in commands: for c in commands:
svr = controller.get_server_obj(c['server_id']['server_id']) svr = self.controller.get_server_obj(c['server_id']['server_id'])
command = c.get('command', None) command = c.get('command', None)
if command == 'start_server': if command == 'start_server':
@ -79,6 +107,9 @@ class TasksManager:
elif command == "restart_server": elif command == "restart_server":
svr.restart_threaded_server() svr.restart_threaded_server()
elif command == "backup_server":
svr.backup_server()
db_helper.mark_command_complete(c.get('command_id', None)) db_helper.mark_command_complete(c.get('command_id', None))
time.sleep(1) time.sleep(1)
@ -88,7 +119,7 @@ class TasksManager:
os.remove(helper.session_file) os.remove(helper.session_file)
os.remove(os.path.join(helper.root_dir, 'exit.txt')) os.remove(os.path.join(helper.root_dir, 'exit.txt'))
os.remove(os.path.join(helper.root_dir, '.header')) os.remove(os.path.join(helper.root_dir, '.header'))
controller.stop_all_servers() self.controller.stop_all_servers()
except: except:
logger.info("Caught error during shutdown", exc_info=True) logger.info("Caught error during shutdown", exc_info=True)
@ -113,6 +144,15 @@ class TasksManager:
logger.info("Launching Scheduler Thread...") logger.info("Launching Scheduler Thread...")
console.info("Launching Scheduler Thread...") console.info("Launching Scheduler Thread...")
self.schedule_thread.start() self.schedule_thread.start()
logger.info("Launching command thread...")
console.info("Launching command thread...")
self.command_thread.start()
logger.info("Launching log watcher...")
console.info("Launching log watcher...")
self.log_watcher_thread.start()
logger.info("Launching realtime thread...")
console.info("Launching realtime thread...")
self.realtime_thread.start()
@staticmethod @staticmethod
def scheduler_thread(): def scheduler_thread():
@ -120,17 +160,16 @@ class TasksManager:
schedule.run_pending() schedule.run_pending()
time.sleep(1) time.sleep(1)
@staticmethod def start_stats_recording(self):
def start_stats_recording():
stats_update_frequency = helper.get_setting('stats_update_frequency') stats_update_frequency = helper.get_setting('stats_update_frequency')
logger.info("Stats collection frequency set to {stats} seconds".format(stats=stats_update_frequency)) logger.info("Stats collection frequency set to {stats} seconds".format(stats=stats_update_frequency))
console.info("Stats collection frequency set to {stats} seconds".format(stats=stats_update_frequency)) console.info("Stats collection frequency set to {stats} seconds".format(stats=stats_update_frequency))
# one for now, # one for now,
stats.record_stats() self.controller.stats.record_stats()
# one for later # one for later
schedule.every(stats_update_frequency).seconds.do(stats.record_stats) schedule.every(stats_update_frequency).seconds.do(self.controller.stats.record_stats).tag('stats-recording')
@staticmethod @staticmethod
def serverjar_cache_refresher(): def serverjar_cache_refresher():
@ -138,7 +177,7 @@ class TasksManager:
server_jar_obj.refresh_cache() server_jar_obj.refresh_cache()
logger.info("Scheduling Serverjars.com cache refresh service every 12 hours") logger.info("Scheduling Serverjars.com cache refresh service every 12 hours")
schedule.every(12).hours.do(server_jar_obj.refresh_cache) schedule.every(12).hours.do(server_jar_obj.refresh_cache).tag('serverjars')
@staticmethod @staticmethod
def realtime(): def realtime():
@ -174,4 +213,5 @@ class TasksManager:
def log_watcher(self): def log_watcher(self):
console.debug('in log_watcher') console.debug('in log_watcher')
helper.check_for_old_logs(db_helper) helper.check_for_old_logs(db_helper)
schedule.every(6).hours.do(lambda: helper.check_for_old_logs(db_helper)) schedule.every(6).hours.do(lambda: helper.check_for_old_logs(db_helper)).tag('log-mgmt')

View File

@ -9,7 +9,6 @@ import shutil
from app.classes.shared.console import console from app.classes.shared.console import console
from app.classes.shared.models import Users, installer from app.classes.shared.models import Users, installer
from app.classes.web.base_handler import BaseHandler from app.classes.web.base_handler import BaseHandler
from app.classes.shared.controller import controller
from app.classes.shared.models import db_helper from app.classes.shared.models import db_helper
from app.classes.shared.helpers import helper from app.classes.shared.helpers import helper
@ -56,8 +55,8 @@ class AjaxHandler(BaseHandler):
logger.warning("Server Data not found in server_log ajax call") logger.warning("Server Data not found in server_log ajax call")
self.redirect("/panel/error?error=Server ID Not Found") self.redirect("/panel/error?error=Server ID Not Found")
if server_data['log_path']: if not server_data['log_path']:
logger.warning("Server ID not found in server_log ajax call ({})".format(server_id)) logger.warning("Log path not found in server_log ajax call ({})".format(server_id))
if full_log: if full_log:
log_lines = helper.get_setting('max_log_lines') log_lines = helper.get_setting('max_log_lines')
@ -149,7 +148,7 @@ class AjaxHandler(BaseHandler):
logger.warning("Server ID not found in send_command ajax call") 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 = controller.get_server_obj(server_id) srv_obj = self.controller.get_server_obj(server_id)
if command: if command:
if srv_obj.check_running(): if srv_obj.check_running():
@ -219,7 +218,6 @@ class AjaxHandler(BaseHandler):
if page == "del_file": if page == "del_file":
file_path = self.get_body_argument('file_path', default=None, strip=True) file_path = self.get_body_argument('file_path', default=None, strip=True)
server_id = self.get_argument('id', None) server_id = self.get_argument('id', None)
print(server_id)
if server_id is None: if server_id is None:
logger.warning("Server ID not found in del_file ajax call") logger.warning("Server ID not found in del_file ajax call")
@ -234,7 +232,9 @@ class AjaxHandler(BaseHandler):
console.warning("Server ID not found in del_file ajax call ({})".format(server_id)) console.warning("Server ID not found in del_file ajax call ({})".format(server_id))
return False return False
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], file_path) \ server_info = db_helper.get_server_data_by_id(server_id)
if not helper.in_path(server_info['path'], file_path) \
or not helper.in_path(server_info['backup_path'], file_path) \
or not helper.check_file_exists(os.path.abspath(file_path)): or not helper.check_file_exists(os.path.abspath(file_path)):
logger.warning("Invalid path in del_file ajax call ({})".format(file_path)) logger.warning("Invalid path in del_file ajax call ({})".format(file_path))
console.warning("Invalid path in del_file ajax call ({})".format(file_path)) console.warning("Invalid path in del_file ajax call ({})".format(file_path))
@ -261,7 +261,9 @@ class AjaxHandler(BaseHandler):
console.warning("Server ID not found in del_file ajax call ({})".format(server_id)) console.warning("Server ID not found in del_file ajax call ({})".format(server_id))
return False return False
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], dir_path) \ server_info = db_helper.get_server_data_by_id(server_id)
if not helper.in_path(server_info['path'], dir_path) \
or not helper.in_path(server_info['backup_path'], dir_path) \
or not helper.check_path_exists(os.path.abspath(dir_path)): or not helper.check_path_exists(os.path.abspath(dir_path)):
logger.warning("Invalid path in del_file ajax call ({})".format(dir_path)) logger.warning("Invalid path in del_file ajax call ({})".format(dir_path))
console.warning("Invalid path in del_file ajax call ({})".format(dir_path)) console.warning("Invalid path in del_file ajax call ({})".format(dir_path))

View File

@ -5,13 +5,13 @@ import tornado.web
import tornado.escape import tornado.escape
import logging import logging
from app.classes.web.base_handler import BaseHandler
from app.classes.shared.models import Users from app.classes.shared.models import Users
from app.classes.minecraft.stats import stats
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class BaseHandler(tornado.web.RequestHandler): class ApiHandler(BaseHandler):
def return_response(self, data: dict): def return_response(self, data: dict):
# Define a standardized response # Define a standardized response
@ -25,6 +25,7 @@ class BaseHandler(tornado.web.RequestHandler):
def authenticate_user(self): def authenticate_user(self):
try: try:
log.debug("Searching for specified token") log.debug("Searching for specified token")
# TODO: YEET THIS
user_data = Users.get(api_token=self.get_argument('token')) user_data = Users.get(api_token=self.get_argument('token'))
log.debug("Checking results") log.debug("Checking results")
if user_data: if user_data:
@ -40,19 +41,19 @@ class BaseHandler(tornado.web.RequestHandler):
pass pass
class ServersStats(BaseHandler): class ServersStats(ApiHandler):
def get(self): def get(self):
"""Get details about all servers""" """Get details about all servers"""
self.authenticate_user() self.authenticate_user()
# Get server stats # Get server stats
self.finish(self.write({"servers": stats.get_servers_stats()})) self.finish(self.write({"servers": self.controller.stats.get_servers_stats()}))
class NodeStats(BaseHandler): class NodeStats(ApiHandler):
def get(self): def get(self):
"""Get stats for particular node""" """Get stats for particular node"""
self.authenticate_user() self.authenticate_user()
# Get node stats # Get node stats
node_stats = stats.get_node_stats() node_stats = self.controller.stats.get_node_stats()
node_stats.pop("servers") node_stats.pop("servers")
self.finish(self.write(node_stats)) self.finish(self.write(node_stats))

View File

@ -1,11 +1,21 @@
import logging import logging
import tornado.web import tornado.web
import bleach
from typing import (
Union,
List,
Optional
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class BaseHandler(tornado.web.RequestHandler): class BaseHandler(tornado.web.RequestHandler):
def initialize(self, controller=None, tasks_manager=None):
self.controller = controller
self.tasks_manager = tasks_manager
def get_remote_ip(self): def get_remote_ip(self):
remote_ip = self.request.headers.get("X-Real-IP") or \ remote_ip = self.request.headers.get("X-Real-IP") or \
self.request.headers.get("X-Forwarded-For") or \ self.request.headers.get("X-Forwarded-For") or \
@ -14,3 +24,28 @@ class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self): def get_current_user(self):
return self.get_secure_cookie("user", max_age_days=1) return self.get_secure_cookie("user", max_age_days=1)
def autobleach(self, text):
if type(text) is bool:
return text
else:
return text
def get_argument(
self,
name: str,
default: Union[None, str, tornado.web._ArgDefaultMarker] = tornado.web._ARG_DEFAULT,
strip: bool = True,
) -> Optional[str]:
arg = self._get_argument(name, default, self.request.arguments, strip)
logger.debug("Bleaching {}: {}".format(name, arg))
return bleach.clean(arg)
def get_arguments(self, name: str, strip: bool = True) -> List[str]:
assert isinstance(strip, bool)
args = self._get_arguments(name, self.request.arguments, strip)
args_ret = []
for arg in args:
logger.debug("Bleaching {}: {}".format(name, arg))
args_ret += bleach.clean(arg)
return args_ret

View File

@ -5,14 +5,13 @@ import tornado.escape
import bleach import bleach
import time import time
import datetime import datetime
import os
from app.classes.shared.console import console from app.classes.shared.console import console
from app.classes.shared.models import Users, installer from app.classes.shared.models import Users, installer
from app.classes.web.base_handler import BaseHandler from app.classes.web.base_handler import BaseHandler
from app.classes.shared.controller import controller
from app.classes.shared.models import db_helper, Servers from app.classes.shared.models import db_helper, Servers
from app.classes.shared.helpers import helper from app.classes.shared.helpers import helper
from app.classes.minecraft.stats import stats
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -50,8 +49,8 @@ class PanelHandler(BaseHandler):
'user_role' : user_role, 'user_role' : user_role,
'server_stats': { 'server_stats': {
'total': len(defined_servers), 'total': len(defined_servers),
'running': len(controller.list_running_servers()), 'running': len(self.controller.list_running_servers()),
'stopped': (len(controller.list_defined_servers()) - len(controller.list_running_servers())) 'stopped': (len(self.controller.list_defined_servers()) - len(self.controller.list_running_servers()))
}, },
'menu_servers': defined_servers, 'menu_servers': defined_servers,
'hosts_data': db_helper.get_latest_hosts_stats(), 'hosts_data': db_helper.get_latest_hosts_stats(),
@ -64,7 +63,7 @@ class PanelHandler(BaseHandler):
if page_data['server_stats']['total'] == 0 and page != "error": if page_data['server_stats']['total'] == 0 and page != "error":
self.set_status(301) self.set_status(301)
self.redirect("/server/step1") self.redirect("/server/step1")
return False return
if page == 'unauthorized': if page == 'unauthorized':
template = "panel/denied.html" template = "panel/denied.html"
@ -73,6 +72,11 @@ class PanelHandler(BaseHandler):
template = "public/error.html" template = "public/error.html"
elif page == 'credits': elif page == 'credits':
with open(helper.credits_cache) as republic_credits_will_do:
credits = json.load(republic_credits_will_do)
page_data["patreons"] = credits["patreons"]
page_data["staff"] = credits["staff"]
page_data["translations"] = credits["translations"]
template = "panel/credits.html" template = "panel/credits.html"
elif page == 'contribute': elif page == 'contribute':
@ -88,7 +92,7 @@ class PanelHandler(BaseHandler):
server_id, server_id,
self.get_remote_ip()) self.get_remote_ip())
controller.remove_server(server_id) self.controller.remove_server(server_id)
self.redirect("/panel/dashboard") self.redirect("/panel/dashboard")
return return
@ -114,14 +118,14 @@ class PanelHandler(BaseHandler):
if server_id is None: if server_id is None:
self.redirect("/panel/error?error=Invalid Server ID") self.redirect("/panel/error?error=Invalid Server ID")
return False return
else: else:
server_id = bleach.clean(server_id) server_id = bleach.clean(server_id)
# does this server id exist? # does this server id exist?
if not db_helper.server_id_exists(server_id): if not db_helper.server_id_exists(server_id):
self.redirect("/panel/error?error=Invalid Server ID") self.redirect("/panel/error?error=Invalid Server ID")
return False return
if user['superuser'] != 1: if user['superuser'] != 1:
#if not db_helper.server_id_authorized(server_id, userId): #if not db_helper.server_id_authorized(server_id, userId):
@ -129,17 +133,21 @@ class PanelHandler(BaseHandler):
self.redirect("/panel/error?error=Invalid Server ID") self.redirect("/panel/error?error=Invalid Server ID")
return False return False
valid_subpages = ['term', 'logs', 'config', 'files', 'admin_controls'] valid_subpages = ['term', 'logs', 'backup', 'config', 'files', 'admin_controls']
if subpage not in valid_subpages: if subpage not in valid_subpages:
logger.debug('not a valid subpage') logger.debug('not a valid subpage')
subpage = 'term' subpage = 'term'
logger.debug('Subpage: "{}"'.format(subpage)) logger.debug('Subpage: "{}"'.format(subpage))
server = self.controller.get_server_obj(server_id)
# server_data isn't needed since the server_stats also pulls server data # server_data isn't needed since the server_stats also pulls server data
# page_data['server_data'] = db_helper.get_server_data_by_id(server_id) page_data['server_data'] = db_helper.get_server_data_by_id(server_id)
page_data['server_stats'] = db_helper.get_server_stats_by_id(server_id) page_data['server_stats'] = db_helper.get_server_stats_by_id(server_id)
page_data['get_players'] = lambda: stats.get_server_players(server_id) page_data['get_players'] = lambda: self.controller.stats.get_server_players(server_id)
if subpage == "backup":
page_data['backup_config'] = db_helper.get_backup_config(server_id)
page_data['backup_list'] = server.list_backups()
def get_banned_players_html(): def get_banned_players_html():
banned_players = helper.get_banned_players(server_id, db_helper) banned_players = helper.get_banned_players(server_id, db_helper)
@ -166,6 +174,79 @@ class PanelHandler(BaseHandler):
# template = "panel/server_details.html" # template = "panel/server_details.html"
template = "panel/server_{subpage}.html".format(subpage=subpage) template = "panel/server_{subpage}.html".format(subpage=subpage)
elif page == 'download_backup':
server_id = self.get_argument('id', None)
file = self.get_argument('file', "")
if server_id is None:
self.redirect("/panel/error?error=Invalid Server ID")
return
else:
server_id = bleach.clean(server_id)
# does this server id exist?
if not db_helper.server_id_exists(server_id):
self.redirect("/panel/error?error=Invalid Server ID")
return
exec_user = db_helper.get_user(user_data['user_id'])
if not exec_user['superuser']:
self.redirect("/panel/error?error=Unauthorized access: not superuser")
return
server_info = db_helper.get_server_data_by_id(server_id)
backup_file = os.path.abspath(os.path.join(server_info["backup_path"], file))
if not helper.in_path(server_info["backup_path"], backup_file) \
or not os.path.isfile(backup_file):
self.redirect("/panel/error?error=Invalid path detected")
return
self.set_header('Content-Type', 'application/octet-stream')
self.set_header('Content-Disposition', 'attachment; filename=' + file)
chunk_size = 1024 * 1024 * 4 # 4 MiB
with open(backup_file, 'rb') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
try:
self.write(chunk) # write the chunk to response
self.flush() # send the chunk to client
except iostream.StreamClosedError:
# this means the client has closed the connection
# so break the loop
break
finally:
# deleting the chunk is very important because
# if many clients are downloading files at the
# same time, the chunks in memory will keep
# increasing and will eat up the RAM
del chunk
self.redirect("/panel/server_detail?id={}&subpage=backup".format(server_id))
elif page == 'backup_now':
server_id = self.get_argument('id', None)
if server_id is None:
self.redirect("/panel/error?error=Invalid Server ID")
return
else:
# does this server id exist?
if not db_helper.server_id_exists(server_id):
self.redirect("/panel/error?error=Invalid Server ID")
return
exec_user = db_helper.get_user(user_data['user_id'])
if not exec_user['superuser']:
self.redirect("/panel/error?error=Unauthorized access: not superuser")
return
server = self.controller.get_server_obj(server_id).backup_server()
self.redirect("/panel/server_detail?id={}&subpage=backup".format(server_id))
elif page == 'panel_config': elif page == 'panel_config':
page_data['users'] = db_helper.get_all_users() page_data['users'] = db_helper.get_all_users()
page_data['roles'] = db_helper.get_all_roles() page_data['roles'] = db_helper.get_all_roles()
@ -194,10 +275,10 @@ class PanelHandler(BaseHandler):
if not exec_user['superuser']: if not exec_user['superuser']:
self.redirect("/panel/error?error=Unauthorized access: not superuser") self.redirect("/panel/error?error=Unauthorized access: not superuser")
return False return
page_data['roles_all'] = db_helper.get_all_roles() page_data['roles_all'] = db_helper.get_all_roles()
page_data['servers_all'] = controller.list_defined_servers() page_data['servers_all'] = self.controller.list_defined_servers()
template = "panel/panel_edit_user.html" template = "panel/panel_edit_user.html"
elif page == "edit_user": elif page == "edit_user":
@ -205,16 +286,16 @@ class PanelHandler(BaseHandler):
user_id = self.get_argument('id', None) user_id = self.get_argument('id', None)
page_data['user'] = db_helper.get_user(user_id) page_data['user'] = db_helper.get_user(user_id)
page_data['roles_all'] = db_helper.get_all_roles() page_data['roles_all'] = db_helper.get_all_roles()
page_data['servers_all'] = controller.list_defined_servers() page_data['servers_all'] = self.controller.list_defined_servers()
exec_user = db_helper.get_user(user_data['user_id']) exec_user = db_helper.get_user(user_data['user_id'])
if not exec_user['superuser']: if not exec_user['superuser']:
self.redirect("/panel/error?error=Unauthorized access: not superuser") self.redirect("/panel/error?error=Unauthorized access: not superuser")
return False return
elif user_id is None: elif user_id is None:
self.redirect("/panel/error?error=Invalid User ID") self.redirect("/panel/error?error=Invalid User ID")
return False return
if exec_user['user_id'] != page_data['user']['user_id']: if exec_user['user_id'] != page_data['user']['user_id']:
page_data['user']['api_token'] = "********" page_data['user']['api_token'] = "********"
@ -228,19 +309,19 @@ class PanelHandler(BaseHandler):
if not exec_user['superuser']: if not exec_user['superuser']:
self.redirect("/panel/error?error=Unauthorized access: not superuser") self.redirect("/panel/error?error=Unauthorized access: not superuser")
return False return
elif user_id is None: elif user_id is None:
self.redirect("/panel/error?error=Invalid User ID") self.redirect("/panel/error?error=Invalid User ID")
return False return
else: else:
# does this user id exist? # does this user id exist?
target_user = db_helper.get_user(user_id) target_user = db_helper.get_user(user_id)
if not target_user: if not target_user:
self.redirect("/panel/error?error=Invalid User ID") self.redirect("/panel/error?error=Invalid User ID")
return False return
elif target_user['superuser']: elif target_user['superuser']:
self.redirect("/panel/error?error=Cannot remove a superuser") self.redirect("/panel/error?error=Cannot remove a superuser")
return False return
db_helper.remove_user(user_id) db_helper.remove_user(user_id)
@ -263,25 +344,25 @@ class PanelHandler(BaseHandler):
if not exec_user['superuser']: if not exec_user['superuser']:
self.redirect("/panel/error?error=Unauthorized access: not superuser") self.redirect("/panel/error?error=Unauthorized access: not superuser")
return False return
page_data['servers_all'] = controller.list_defined_servers() page_data['servers_all'] = self.controller.list_defined_servers()
template = "panel/panel_edit_role.html" template = "panel/panel_edit_role.html"
elif page == "edit_role": elif page == "edit_role":
page_data['new_role'] = False page_data['new_role'] = False
role_id = self.get_argument('id', None) role_id = self.get_argument('id', None)
page_data['role'] = db_helper.get_role(role_id) page_data['role'] = db_helper.get_role(role_id)
page_data['servers_all'] = controller.list_defined_servers() page_data['servers_all'] = self.controller.list_defined_servers()
exec_user = db_helper.get_user(user_data['user_id']) exec_user = db_helper.get_user(user_data['user_id'])
if not exec_user['superuser']: if not exec_user['superuser']:
self.redirect("/panel/error?error=Unauthorized access: not superuser") self.redirect("/panel/error?error=Unauthorized access: not superuser")
return False return
elif role_id is None: elif role_id is None:
self.redirect("/panel/error?error=Invalid Role ID") self.redirect("/panel/error?error=Invalid Role ID")
return False return
template = "panel/panel_edit_role.html" template = "panel/panel_edit_role.html"
@ -293,16 +374,16 @@ class PanelHandler(BaseHandler):
if not exec_user['superuser']: if not exec_user['superuser']:
self.redirect("/panel/error?error=Unauthorized access: not superuser") self.redirect("/panel/error?error=Unauthorized access: not superuser")
return False return
elif role_id is None: elif role_id is None:
self.redirect("/panel/error?error=Invalid Role ID") self.redirect("/panel/error?error=Invalid Role ID")
return False return
else: else:
# does this user id exist? # does this user id exist?
target_role = db_helper.get_user(role_id) target_role = db_helper.get_user(role_id)
if not target_role: if not target_role:
self.redirect("/panel/error?error=Invalid Role ID") self.redirect("/panel/error?error=Invalid Role ID")
return False return
db_helper.remove_role(role_id) db_helper.remove_role(role_id)
@ -348,17 +429,15 @@ class PanelHandler(BaseHandler):
if not exec_user['superuser']: if not exec_user['superuser']:
self.redirect("/panel/error?error=Unauthorized access: not superuser") self.redirect("/panel/error?error=Unauthorized access: not superuser")
return False return
elif server_id is None: elif server_id is None:
self.redirect("/panel/error?error=Invalid Server ID") self.redirect("/panel/error?error=Invalid Server ID")
return False return
else: else:
server_id = bleach.clean(server_id)
# does this server id exist? # does this server id exist?
if not db_helper.server_id_exists(server_id): if not db_helper.server_id_exists(server_id):
self.redirect("/panel/error?error=Invalid Server ID") self.redirect("/panel/error?error=Invalid Server ID")
return False return
Servers.update({ Servers.update({
Servers.server_name: server_name, Servers.server_name: server_name,
@ -375,7 +454,7 @@ class PanelHandler(BaseHandler):
Servers.logs_delete_after: logs_delete_after, Servers.logs_delete_after: logs_delete_after,
}).where(Servers.server_id == server_id).execute() }).where(Servers.server_id == server_id).execute()
controller.refresh_server_settings(server_id) self.controller.refresh_server_settings(server_id)
db_helper.add_to_audit_log(user_data['user_id'], db_helper.add_to_audit_log(user_data['user_id'],
"Edited server {} named {}".format(server_id, server_name), "Edited server {} named {}".format(server_id, server_name),
@ -384,6 +463,41 @@ class PanelHandler(BaseHandler):
self.redirect("/panel/server_detail?id={}&subpage=config".format(server_id)) self.redirect("/panel/server_detail?id={}&subpage=config".format(server_id))
if page == "server_backup":
logger.debug(self.request.arguments)
server_id = self.get_argument('id', None)
backup_path = bleach.clean(self.get_argument('backup_path', None))
max_backups = bleach.clean(self.get_argument('max_backups', None))
enabled = int(float(bleach.clean(self.get_argument('auto_enabled'), '0')))
user_data = json.loads(self.get_secure_cookie("user_data"))
exec_user = db_helper.get_user(user_data['user_id'])
if not exec_user['superuser']:
self.redirect("/panel/error?error=Unauthorized access: not superuser")
return
elif server_id is None:
self.redirect("/panel/error?error=Invalid Server ID")
return
else:
# does this server id exist?
if not db_helper.server_id_exists(server_id):
self.redirect("/panel/error?error=Invalid Server ID")
return
if backup_path is not None:
Servers.update({
Servers.backup_path: backup_path
}).where(Servers.server_id == server_id).execute()
db_helper.set_backup_config(server_id, max_backups=max_backups)
db_helper.add_to_audit_log(user_data['user_id'],
"Edited server {}: updated backups".format(server_id),
server_id,
self.get_remote_ip())
self.tasks_manager.reload_schedule_from_db()
self.redirect("/panel/server_detail?id={}&subpage=backup".format(server_id))
elif page == "edit_user": elif page == "edit_user":
user_id = bleach.clean(self.get_argument('id', None)) user_id = bleach.clean(self.get_argument('id', None))
username = bleach.clean(self.get_argument('username', None)) username = bleach.clean(self.get_argument('username', None))
@ -397,22 +511,22 @@ class PanelHandler(BaseHandler):
if not exec_user['superuser']: if not exec_user['superuser']:
self.redirect("/panel/error?error=Unauthorized access: not superuser") self.redirect("/panel/error?error=Unauthorized access: not superuser")
return False return
elif username is None or username == "": elif username is None or username == "":
self.redirect("/panel/error?error=Invalid username") self.redirect("/panel/error?error=Invalid username")
return False return
elif user_id is None: elif user_id is None:
self.redirect("/panel/error?error=Invalid User ID") self.redirect("/panel/error?error=Invalid User ID")
return False return
else: else:
# does this user id exist? # does this user id exist?
if not db_helper.user_id_exists(user_id): if not db_helper.user_id_exists(user_id):
self.redirect("/panel/error?error=Invalid User ID") self.redirect("/panel/error?error=Invalid User ID")
return False return
if password0 != password1: if password0 != password1:
self.redirect("/panel/error?error=Passwords must match") self.redirect("/panel/error?error=Passwords must match")
return False return
roles = set() roles = set()
for role in db_helper.get_all_roles(): for role in db_helper.get_all_roles():
@ -425,7 +539,7 @@ class PanelHandler(BaseHandler):
roles.add(role.role_id) roles.add(role.role_id)
servers = set() servers = set()
for server in controller.list_defined_servers(): for server in self.controller.list_defined_servers():
argument = int(float( argument = int(float(
bleach.clean( bleach.clean(
self.get_argument('server_{}_access'.format(server['server_id']), '0') self.get_argument('server_{}_access'.format(server['server_id']), '0')
@ -461,19 +575,19 @@ class PanelHandler(BaseHandler):
exec_user = db_helper.get_user(user_data['user_id']) exec_user = db_helper.get_user(user_data['user_id'])
if not exec_user['superuser']: if not exec_user['superuser']:
self.redirect("/panel/error?error=Unauthorized access: not superuser") self.redirect("/panel/error?error=Unauthorized access: not superuser")
return False return
elif username is None or username == "": elif username is None or username == "":
self.redirect("/panel/error?error=Invalid username") self.redirect("/panel/error?error=Invalid username")
return False return
else: else:
# does this user id exist? # does this user id exist?
if db_helper.get_userid_by_name(username) is not None: if db_helper.get_userid_by_name(username) is not None:
self.redirect("/panel/error?error=User exists") self.redirect("/panel/error?error=User exists")
return False return
if password0 != password1: if password0 != password1:
self.redirect("/panel/error?error=Passwords must match") self.redirect("/panel/error?error=Passwords must match")
return False return
roles = set() roles = set()
for role in db_helper.get_all_roles(): for role in db_helper.get_all_roles():
@ -486,7 +600,7 @@ class PanelHandler(BaseHandler):
roles.add(role['role_id']) roles.add(role['role_id'])
servers = set() servers = set()
for server in controller.list_defined_servers(): for server in self.controller.list_defined_servers():
argument = int(float( argument = int(float(
bleach.clean( bleach.clean(
self.get_argument('server_{}_access'.format(server['server_id']), '0') self.get_argument('server_{}_access'.format(server['server_id']), '0')
@ -517,21 +631,21 @@ class PanelHandler(BaseHandler):
if not exec_user['superuser']: if not exec_user['superuser']:
self.redirect("/panel/error?error=Unauthorized access: not superuser") self.redirect("/panel/error?error=Unauthorized access: not superuser")
return False return
elif role_name is None or role_name == "": elif role_name is None or role_name == "":
self.redirect("/panel/error?error=Invalid username") self.redirect("/panel/error?error=Invalid username")
return False return
elif role_id is None: elif role_id is None:
self.redirect("/panel/error?error=Invalid Role ID") self.redirect("/panel/error?error=Invalid Role ID")
return False return
else: else:
# does this user id exist? # does this user id exist?
if not db_helper.role_id_exists(role_id): if not db_helper.role_id_exists(role_id):
self.redirect("/panel/error?error=Invalid Role ID") self.redirect("/panel/error?error=Invalid Role ID")
return False return
servers = set() servers = set()
for server in controller.list_defined_servers(): for server in self.controller.list_defined_servers():
argument = int(float( argument = int(float(
bleach.clean( bleach.clean(
self.get_argument('server_{}_access'.format(server['server_id']), '0') self.get_argument('server_{}_access'.format(server['server_id']), '0')
@ -560,18 +674,18 @@ class PanelHandler(BaseHandler):
exec_user = db_helper.get_user(user_data['user_id']) exec_user = db_helper.get_user(user_data['user_id'])
if not exec_user['superuser']: if not exec_user['superuser']:
self.redirect("/panel/error?error=Unauthorized access: not superuser") self.redirect("/panel/error?error=Unauthorized access: not superuser")
return False return
elif role_name is None or role_name == "": elif role_name is None or role_name == "":
self.redirect("/panel/error?error=Invalid role name") self.redirect("/panel/error?error=Invalid role name")
return False return
else: else:
# does this user id exist? # does this user id exist?
if db_helper.get_roleid_by_name(role_name) is not None: if db_helper.get_roleid_by_name(role_name) is not None:
self.redirect("/panel/error?error=Role exists") self.redirect("/panel/error?error=Role exists")
return False return
servers = set() servers = set()
for server in controller.list_defined_servers(): for server in self.controller.list_defined_servers():
argument = int(float( argument = int(float(
bleach.clean( bleach.clean(
self.get_argument('server_{}_access'.format(server['server_id']), '0') self.get_argument('server_{}_access'.format(server['server_id']), '0')
@ -592,3 +706,7 @@ class PanelHandler(BaseHandler):
server_id=0, server_id=0,
source_ip=self.get_remote_ip()) source_ip=self.get_remote_ip())
self.redirect("/panel/panel_config") self.redirect("/panel/panel_config")
else:
self.set_status(404)
self.render("public/404.html")

View File

@ -6,10 +6,8 @@ import shutil
from app.classes.shared.console import console from app.classes.shared.console import console
from app.classes.web.base_handler import BaseHandler from app.classes.web.base_handler import BaseHandler
from app.classes.shared.controller import controller
from app.classes.shared.models import db_helper, Servers from app.classes.shared.models import db_helper, Servers
from app.classes.minecraft.serverjars import server_jar_obj from app.classes.minecraft.serverjars import server_jar_obj
from app.classes.minecraft.stats import stats
from app.classes.shared.helpers import helper from app.classes.shared.helpers import helper
@ -48,16 +46,16 @@ class ServerHandler(BaseHandler):
template = "public/404.html" template = "public/404.html"
defined_servers = controller.list_defined_servers() defined_servers = self.controller.list_defined_servers()
page_data = { page_data = {
'version_data': helper.get_version_string(), 'version_data': helper.get_version_string(),
'user_data': user_data, 'user_data': user_data,
'user_role' : user_role, 'user_role' : user_role,
'server_stats': { 'server_stats': {
'total': len(controller.list_defined_servers()), 'total': len(self.controller.list_defined_servers()),
'running': len(controller.list_running_servers()), 'running': len(self.controller.list_running_servers()),
'stopped': (len(controller.list_defined_servers()) - len(controller.list_running_servers())) 'stopped': (len(self.controller.list_defined_servers()) - len(self.controller.list_running_servers()))
}, },
'hosts_data': db_helper.get_latest_hosts_stats(), 'hosts_data': db_helper.get_latest_hosts_stats(),
'menu_servers': defined_servers, 'menu_servers': defined_servers,
@ -144,7 +142,7 @@ class ServerHandler(BaseHandler):
Servers.stop_command: stop_command Servers.stop_command: stop_command
}).execute() }).execute()
controller.init_all_servers() self.controller.init_all_servers()
console.debug('initted all servers') console.debug('initted all servers')
return return
@ -164,26 +162,26 @@ class ServerHandler(BaseHandler):
server_parts = server.split("|") server_parts = server.split("|")
if import_type == 'import_jar': if import_type == 'import_jar':
good_path = controller.verify_jar_server(import_server_path, import_server_jar) good_path = self.controller.verify_jar_server(import_server_path, import_server_jar)
if not good_path: if not good_path:
self.redirect("/panel/error?error=Server path or Server Jar not found!") self.redirect("/panel/error?error=Server path or Server Jar not found!")
return False return False
new_server_id = controller.import_jar_server(server_name, import_server_path,import_server_jar, min_mem, max_mem, port) new_server_id = self.controller.import_jar_server(server_name, import_server_path,import_server_jar, min_mem, max_mem, port)
elif import_type == 'import_zip': elif import_type == 'import_zip':
good_path = controller.verify_zip_server(import_server_path) good_path = self.controller.verify_zip_server(import_server_path)
if not good_path: if not good_path:
self.redirect("/panel/error?error=Zip file not found!") self.redirect("/panel/error?error=Zip file not found!")
return False return False
new_server_id = controller.import_zip_server(server_name, import_server_path,import_server_jar, min_mem, max_mem, port) new_server_id = self.controller.import_zip_server(server_name, import_server_path,import_server_jar, min_mem, max_mem, port)
if new_server_id == "false": if new_server_id == "false":
self.redirect("/panel/error?error=ZIP file not accessible! You can fix this permissions issue with sudo chown -R crafty:crafty {} And sudo chmod 2775 -R {}".format(import_server_path, import_server_path)) self.redirect("/panel/error?error=ZIP file not accessible! You can fix this permissions issue with sudo chown -R crafty:crafty {} And sudo chmod 2775 -R {}".format(import_server_path, import_server_path))
return False return False
else: else:
# todo: add server type check here and call the correct server add functions if not a jar # todo: add server type check here and call the correct server add functions if not a jar
new_server_id = controller.create_jar_server(server_parts[0], server_parts[1], server_name, min_mem, max_mem, port) new_server_id = self.controller.create_jar_server(server_parts[0], server_parts[1], server_name, min_mem, max_mem, port)
if new_server_id: if new_server_id:
db_helper.add_to_audit_log(user_data['user_id'], db_helper.add_to_audit_log(user_data['user_id'],
@ -194,7 +192,7 @@ class ServerHandler(BaseHandler):
logger.error("Unable to create server") logger.error("Unable to create server")
console.error("Unable to create server") console.error("Unable to create server")
stats.record_stats() self.controller.stats.record_stats()
self.redirect("/panel/dashboard") self.redirect("/panel/dashboard")
self.render( self.render(

View File

@ -33,12 +33,14 @@ except ModuleNotFoundError as e:
class webserver: class Webserver:
def __init__(self): def __init__(self, controller, tasks_manager):
self.ioloop = None self.ioloop = None
self.HTTP_Server = None self.HTTP_Server = None
self.HTTPS_Server = None self.HTTPS_Server = None
self.controller = controller
self.tasks_manager = tasks_manager
self._asyncio_patch() self._asyncio_patch()
@ -118,15 +120,16 @@ class webserver:
tornado.locale.set_default_locale(lang) tornado.locale.set_default_locale(lang)
handler_args = {"controller": self.controller, "tasks_manager": self.tasks_manager}
handlers = [ handlers = [
(r'/', DefaultHandler), (r'/', DefaultHandler, handler_args),
(r'/public/(.*)', PublicHandler), (r'/public/(.*)', PublicHandler, handler_args),
(r'/panel/(.*)', PanelHandler), (r'/panel/(.*)', PanelHandler, handler_args),
(r'/server/(.*)', ServerHandler), (r'/server/(.*)', ServerHandler, handler_args),
(r'/ajax/(.*)', AjaxHandler), (r'/ajax/(.*)', AjaxHandler, handler_args),
(r'/api/stats/servers', ServersStats), (r'/api/stats/servers', ServersStats, handler_args),
(r'/api/stats/node', NodeStats), (r'/api/stats/node', NodeStats, handler_args),
(r'/ws', SocketHandler), (r'/ws', SocketHandler, handler_args),
] ]
app = tornado.web.Application( app = tornado.web.Application(

View File

@ -8,6 +8,10 @@ from app.classes.web.websocket_helper import websocket_helper
class SocketHandler(tornado.websocket.WebSocketHandler): class SocketHandler(tornado.websocket.WebSocketHandler):
def initialize(self, controller=None, tasks_manager=None):
self.controller = controller
self.tasks_manager = tasks_manager
def get_remote_ip(self): def get_remote_ip(self):
remote_ip = self.request.headers.get("X-Real-IP") or \ remote_ip = self.request.headers.get("X-Real-IP") or \
self.request.headers.get("X-Forwarded-For") or \ self.request.headers.get("X-Forwarded-For") or \

137
app/config/credits.json Normal file
View File

@ -0,0 +1,137 @@
{
"patreons": [
{
"name": "Richard B",
"level": "substainer"
},
{
"name": "John C",
"level": "advocate"
},
{
"name": "Nicolas T",
"level": "substainer"
},
{
"name": "Lightkeeper",
"level": "substainer"
},
{
"name": "test user 1",
"level": "supporter"
},
{
"name": "test user 2",
"level": "other"
}
],
"staff": {
"development": [
{
"name": "Phil Tarrant",
"title": "Benevolent Dictator for Life",
"loc": "Atlanta, GA",
"tags": [ "Staff", "Developer", [ "BDFL", "https://en.wikipedia.org/wiki/Benevolent_dictator_for_life" ] ],
"blurb": "For best results, apply a thin layer of Phillip to code, cyber security, parenthood for maximum effectiveness. Phillip often spends too much time with his chickens.",
"pic": "/static/assets/images/credits/ptarrant_cropped.png"
},
{
"name": "Pita Bread",
"title": null,
"loc": "Midwest, USA",
"tags": [ "Staff", null, "Community Leader" ],
"blurb": "His interests include bread, Linux, and networking. He enjoys pumpkins, organizing, and long-winded emails, but hates wifi.",
"pic": "/static/assets/images/credits/pita_cropped.png"
},
{
"name": "macgeek",
"title": null,
"loc": "Midwest, USA",
"tags": [ "Staff", "Developer", "Project Manager" ],
"blurb": "Sysadmin for work and sysadmin for fun (avid homelabber). He enjoys a good tech tangent and gaming.",
"pic": "/static/assets/images/credits/macgeek_cropped.png"
},
{
"name": "parzivaldewey",
"title": null,
"loc": "East Coast, USA",
"tags": [ "Staff", "Developer", "Support Manager" ],
"blurb": "His interests include Linux, gaming, and helping others. When he's able to unplug he enjoys biking, hiking, and playing soccer.",
"pic": "/static/assets/images/credits/andrew_cropped.png"
},
{
"name": "MC Gaming",
"title": null,
"loc": "Central, UK",
"tags": [ "Staff", "Developer", null ],
"blurb": "His interests include learning, Linux, programming. He loves pentesting apps and gaming.",
"pic": "/static/assets/images/credits/mcgaming.png"
},
{
"name": "Silversthorn",
"title": null,
"loc": null,
"tags": [ "Staff", "Developer", null ],
"blurb": "Often in it's cave, he sometimes goes out to help or do silly jokes. He's an IT clown (not the film), but seriously do the job when it's needed.",
"pic": "/static/assets/images/credits/silversthorn.png"
},
{
"name": "ThatOneLukas",
"title": null,
"loc": "Helsinki, FI",
"tags": [ "Staff", "Developer", null ],
"blurb": "Lukas enjoys bashing his head at the table while his code does not work",
"pic": "/static/assets/images/credits/lukas_cropped.png"
}
],
"support": [
{
"name": "iSilverfyre",
"title": null,
"loc": null,
"tags": [ "Staff", "Wiki", null ],
"blurb": "Silver enjoys helping others with their computer needs, writing documentation and loving her cat.",
"pic": "/static/assets/images/credits/isilverfyre.png"
},
{
"name": "Quentin",
"title": null,
"loc": null,
"tags": [ "Staff", "Developer", null ],
"blurb": "Hosts Minecraft servers for his weird friends, works for an IoT company as his dayjob. The 's' in IoT stands for 'secure'.",
"pic": "/static/assets/images/credits/qub3d.png"
}
],
"retired": [
{
"name": "Kev Dagoat",
"title": "Head of Development",
"loc": "East Coast, AU",
"tags": [ "Staff", "Developer", "HOD" ],
"blurb": "His interests include Linux, programming, and goats of course. He enjoys building APIs, K8s, and Geeking over video cards.",
"pic": "/static/assets/images/credits/kevdagoat.jpeg"
},
{
"name": "Manu",
"title": null,
"loc": "Eastern, CA",
"tags": [ "Staff", "Developer", "Project Manager" ],
"blurb": "His interests include learning, Linux, and programming. He enjoys speaking French, doing 6 things at once, and testing software.",
"pic": "/static/assets/images/credits/manu_cropped.png"
},
{
"name": "UltraBlack",
"title": null,
"loc": "Bavaria, DE",
"tags": [ "Staff", null, "Idea Manager" ],
"blurb": "Hi, my name is Tim, and I'm a huge fan of linux. I'm often gaming and occasionally coding.",
"pic": "/static/assets/images/credits/ultrablack_cropped.png"
}
]
},
"translations": {
"UltraBlack": [ "German" ],
"Manu": [ "French" ],
"ptarrant": [ "Sarcasm", "Wit" ]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -27,9 +27,76 @@
<div class="col-md-12 grid-margin"> <div class="col-md-12 grid-margin">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="far fa-code"></i> &nbsp;Development Team</h4>
</div>
<div class="row">
{% for person in data['staff']['development'] %}
<div class="col-md-6 mb-5">
<div class="card rounded shadow-none">
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="user-avatar mb-auto">
<img src="{{ person['pic'] }}"
alt="profile image" class="profile-img img-lg rounded-circle">
</div>
<div class="wrapper">
<div class="wrapper d-flex align-items-center">
<h4 class="mb-0 font-weight-medium">{{ person['name'] }}</h4>
</div>
<div class="wrapper d-flex align-items-center font-weight-medium text-muted">
{% if person['loc'] %}
<i class="mdi mdi-map-marker-outline mr-2"></i>
<p class="mb-0 text-muted">{{ person['loc'] }}</p>
{% end %}
</div>
</div>
</div>
<div class="col-md-8">
<div class="wrapper d-flex align-items-start">
{% if person['tags'][0] %}
<span class="btn btn-sm btn-info mr-2">{{ person['tags'][0] }}</span>
{% end %}
{% if person['tags'][1] %}
<span class="btn btn-sm btn-primary mr-2">{{ person['tags'][1] }}</span>
{% end %}
{% if person['tags'][2] %}
{% if type(person['tags'][2]) is list %}
<a href="{{ person['tags'][2][1] }}" class="btn btn-sm btn-inverse-success mr-2">{{ person['tags'][2][0] }}</a>
{% else %}
<span class="btn btn-sm btn-inverse-success mr-2">{{ person['tags'][2] }}</span>
{% end %}
{% end %}
</div>
<div class="wrapper d-flex align-items-start pt-3">
{% if person['title'] %}
Crafty's {{ person['title'] }}<br /><br />
{% end %}
{{ person['blurb'] }}
</div>
</div>
</div>
</div>
</div>
</div>
{% end %}
</div> <!-- end of user row -->
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fa fa-book"></i> &nbsp;Support and Documentation Team</h4>
</div>
<div class="row"> <div class="row">
{% for person in data['staff']['support'] %}
<div class="col-md-6 mb-5"> <div class="col-md-6 mb-5">
<div class="card rounded shadow-none"> <div class="card rounded shadow-none">
<div class="card-body"> <div class="card-body">
@ -38,74 +105,46 @@
<div class="col-md-4"> <div class="col-md-4">
<div class="user-avatar mb-auto"> <div class="user-avatar mb-auto">
<img src="/static/assets/images/credits/ptarrant_cropped.png" <img src="{{ person['pic'] }}"
alt="profile image" class="profile-img img-lg rounded-circle"> alt="profile image" class="profile-img img-lg rounded-circle">
</div> </div>
<div class="wrapper"> <div class="wrapper">
<div class="wrapper d-flex align-items-center"> <div class="wrapper d-flex align-items-center">
<h4 class="mb-0 font-weight-medium">Phillip Tarrant</h4> <h4 class="mb-0 font-weight-medium">{{ person['name'] }}</h4>
</div> </div>
<div class="wrapper d-flex align-items-center font-weight-medium text-muted"> <div class="wrapper d-flex align-items-center font-weight-medium text-muted">
{% if person['loc'] %}
<i class="mdi mdi-map-marker-outline mr-2"></i> <i class="mdi mdi-map-marker-outline mr-2"></i>
<p class="mb-0 text-muted">Atlanta, GA</p> <p class="mb-0 text-muted">{{ person['loc'] }}</p>
{% end %}
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<div class="wrapper d-flex align-items-start"> <div class="wrapper d-flex align-items-start">
<span class="btn btn-sm btn-info mr-2">Staff</span> {% if person['tags'][0] %}
<span class="btn btn-sm btn-primary mr-2">Developer</span> <span class="btn btn-sm btn-info mr-2">{{ person['tags'][0] }}</span>
<a href="https://en.wikipedia.org/wiki/Benevolent_dictator_for_life" class="btn btn-sm btn-inverse-success mr-2">BDFL</a> {% end %}
{% if person['tags'][1] %}
<span class="btn btn-sm btn-primary mr-2">{{ person['tags'][1] }}</span>
{% end %}
{% if person['tags'][2] %}
{% if type(person['tags'][2]) is list %}
<a href="{{ person['tags'][2][1] }}" class="btn btn-sm btn-inverse-success mr-2">{{ person['tags'][2][0] }}</a>
{% else %}
<span class="btn btn-sm btn-inverse-success mr-2">{{ person['tags'][2] }}</span>
{% end %}
{% end %}
</div> </div>
<div class="wrapper d-flex align-items-start pt-3"> <div class="wrapper d-flex align-items-start pt-3">
Crafty's Benevolent Dictator for Life.<br /><br /> {% if person['title'] %}
His interests include Linux, cybersecurity, hacking, and gaming. Crafty's {{ person['title'] }}<br /><br />
He enjoys downtime with the family, and playing with his chickens. {% end %}
</div> {{ person['blurb'] }}
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-5">
<div class="card rounded shadow-none">
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="user-avatar mb-auto">
<img src="/static/assets/images/credits/pita_cropped.png"
alt="profile image" class="profile-img img-lg rounded-circle">
</div>
<div class="wrapper">
<div class="wrapper d-flex align-items-center">
<h4 class="mb-0 font-weight-medium">Pita Bread</h4>
</div>
<div class="wrapper d-flex align-items-center font-weight-medium text-muted">
<i class="mdi mdi-map-marker-outline mr-2"></i>
<p class="mb-0 text-muted">Midwest, USA</p>
</div>
</div>
</div>
<div class="col-md-8">
<div class="wrapper d-flex align-items-start">
<span class="btn btn-sm btn-info mr-2">Staff</span>
<span class="btn btn-sm btn-inverse-success mr-2">Community Leader</span>
</div>
<div class="wrapper d-flex align-items-start pt-3">
His interests include bread, Linux, and networking.
He enjoys pumpkins, organizing, and long-winded emails, but hates wifi.
</div> </div>
</div> </div>
</div> </div>
@ -113,99 +152,17 @@
</div> </div>
</div> </div>
</div> </div>
{% end %}
</div> <!-- end user row--> </div> <!-- end user row-->
<div class="row"> <div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="far fa-server"></i> &nbsp;Retired Staff</h4>
<div class="col-md-6 mb-5"> </div>
<div class="card rounded shadow-none">
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="user-avatar mb-auto">
<img src="/static/assets/images/credits/kevdagoat.jpeg"
alt="profile image" class="profile-img img-lg rounded-circle">
</div>
<div class="wrapper">
<div class="wrapper d-flex align-items-center">
<h4 class="mb-0 font-weight-medium">Kev Dagoat</h4>
</div>
<div class="wrapper d-flex align-items-center font-weight-medium text-muted">
<i class="mdi mdi-map-marker-outline mr-2"></i>
<p class="mb-0 text-muted">East Coast, AU</p>
</div>
</div>
</div>
<div class="col-md-8">
<div class="wrapper d-flex align-items-start">
<span class="btn btn-sm btn-info mr-2">Staff</span>
<span class="btn btn-sm btn-primary mr-2">Developer</span>
<span class="btn btn-sm btn-inverse-success mr-2">HOD</span>
</div>
<div class="wrapper d-flex align-items-start pt-3">
Crafty's Head Of Development<br /><br />
His interests include Linux, programming, and goats of course.
He enjoys building APIs, K8s, and Geeking over video cards.
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-5">
<div class="card rounded shadow-none">
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="user-avatar mb-auto">
<img src="/static/assets/images/credits/mcgaming.png"
alt="profile image" class="profile-img img-lg rounded-circle">
</div>
<div class="wrapper">
<div class="wrapper d-flex align-items-center">
<h4 class="mb-0 font-weight-medium">MC Gaming</h4>
</div>
<div class="wrapper d-flex align-items-center font-weight-medium text-muted">
<i class="mdi mdi-map-marker-outline mr-2"></i>
<p class="mb-0 text-muted">Central, UK</p>
</div>
</div>
</div>
<div class="col-md-8">
<div class="wrapper d-flex align-items-start">
<span class="btn btn-sm btn-info mr-2">Staff</span>
<span class="btn btn-sm btn-primary mr-2">Developer</span>
</div>
<div class="wrapper d-flex align-items-start pt-3">
His interests include learning, Linux, programming.
He loves pentesting apps and gaming.
</div>
</div>
</div>
</div>
</div>
</div>
</div> <!-- end user row-->
<div class="row"> <div class="row">
{% for person in data['staff']['retired'] %}
<div class="col-md-6 mb-5"> <div class="col-md-6 mb-5">
<div class="card rounded shadow-none"> <div class="card rounded shadow-none">
<div class="card-body"> <div class="card-body">
@ -214,200 +171,46 @@
<div class="col-md-4"> <div class="col-md-4">
<div class="user-avatar mb-auto"> <div class="user-avatar mb-auto">
<img src="/static/assets/images/credits/andrew_cropped.png" <img src="{{ person['pic'] }}"
alt="profile image" class="profile-img img-lg rounded-circle"> alt="profile image" class="profile-img img-lg rounded-circle">
</div> </div>
<div class="wrapper"> <div class="wrapper">
<div class="wrapper d-flex align-items-center"> <div class="wrapper d-flex align-items-center">
<h4 class="mb-0 font-weight-medium">Andrew Redacted</h4> <h4 class="mb-0 font-weight-medium">{{ person['name'] }}</h4>
</div> </div>
<div class="wrapper d-flex align-items-center font-weight-medium text-muted"> <div class="wrapper d-flex align-items-center font-weight-medium text-muted">
{% if person['loc'] %}
<i class="mdi mdi-map-marker-outline mr-2"></i> <i class="mdi mdi-map-marker-outline mr-2"></i>
<p class="mb-0 text-muted">East Coast, USA</p> <p class="mb-0 text-muted">{{ person['loc'] }}</p>
{% end %}
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<div class="wrapper d-flex align-items-start"> <div class="wrapper d-flex align-items-start">
<span class="btn btn-sm btn-info mr-2">Staff</span> {% if person['tags'][0] %}
<span class="btn btn-sm btn-inverse-success mr-2">Support Manager</span> <span class="btn btn-sm btn-info mr-2">{{ person['tags'][0] }}</span>
{% end %}
{% if person['tags'][1] %}
<span class="btn btn-sm btn-primary mr-2">{{ person['tags'][1] }}</span>
{% end %}
{% if person['tags'][2] %}
{% if type(person['tags'][2]) is list %}
<a href="{{ person['tags'][2][1] }}" class="btn btn-sm btn-inverse-success mr-2">{{ person['tags'][2][0] }}</a>
{% else %}
<span class="btn btn-sm btn-inverse-success mr-2">{{ person['tags'][2] }}</span>
{% end %}
{% end %}
</div> </div>
<div class="wrapper d-flex align-items-start pt-3"> <div class="wrapper d-flex align-items-start pt-3">
His interests include Linux, gaming, and helping others. When he's able to {% if person['title'] %}
unplug he enjoys biking, hiking, and playing soccer. Crafty's {{ person['title'] }}<br /><br />
</div> {% end %}
</div> {{ person['blurb'] }}
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-5">
<div class="card rounded shadow-none">
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="user-avatar mb-auto">
<img src="/static/assets/images/credits/manu_cropped.png"
alt="profile image" class="profile-img img-lg rounded-circle">
</div>
<div class="wrapper">
<div class="wrapper d-flex align-items-center">
<h4 class="mb-0 font-weight-medium">Manu Redacted</h4>
</div>
<div class="wrapper d-flex align-items-center font-weight-medium text-muted">
<i class="mdi mdi-map-marker-outline mr-2"></i>
<p class="mb-0 text-muted">Eastern, CA</p>
</div>
</div>
</div>
<div class="col-md-8">
<div class="wrapper d-flex align-items-start">
<span class="btn btn-sm btn-info mr-2">Staff</span>
<span class="btn btn-sm btn-primary mr-2">Developer</span>
<span class="btn btn-sm btn-inverse-success mr-2">Project Manager</span>
</div>
<div class="wrapper d-flex align-items-start pt-3">
His interests include learning, Linux, and programming.
He enjoys speaking French, doing 6 things at once, and testing software.
</div>
</div>
</div>
</div>
</div>
</div>
</div> <!-- end user row-->
<div class="row">
<div class="col-md-6 mb-5">
<div class="card rounded shadow-none">
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="user-avatar mb-auto">
<img src="/static/assets/images/credits/ultrablack_cropped.png"
alt="profile image" class="profile-img img-lg rounded-circle">
</div>
<div class="wrapper">
<div class="wrapper d-flex align-items-center">
<h4 class="mb-0 font-weight-medium">UltraBlack</h4>
</div>
<div class="wrapper d-flex align-items-center font-weight-medium text-muted">
<i class="mdi mdi-map-marker-outline mr-2"></i>
<p class="mb-0 text-muted">Bavaria, DE</p>
</div>
</div>
</div>
<div class="col-md-8">
<div class="wrapper d-flex align-items-start">
<span class="btn btn-sm btn-info mr-2">Staff</span>
<span class="btn btn-sm btn-inverse-success mr-2">Idea Manager</span>
</div>
<div class="wrapper d-flex align-items-start pt-3">
Hi, my name is Tim, and I'm a huge fan of linux.
I'm often gaming and occasionally coding.
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-5">
<div class="card rounded shadow-none">
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="user-avatar mb-auto">
<img src="/static/assets/images/credits/macgeek_cropped.png"
alt="profile image" class="profile-img img-lg rounded-circle">
</div>
<div class="wrapper">
<div class="wrapper d-flex align-items-center">
<h4 class="mb-0 font-weight-medium">Mac Geek</h4>
</div>
<div class="wrapper d-flex align-items-center font-weight-medium text-muted">
<i class="mdi mdi-map-marker-outline mr-2"></i>
<p class="mb-0 text-muted">Eastern, USA</p>
</div>
</div>
</div>
<div class="col-md-8">
<div class="wrapper d-flex align-items-start">
<span class="btn btn-sm btn-info mr-2">Staff</span>
<span class="btn btn-sm btn-primary mr-2">Developer</span>
<span class="btn btn-sm btn-inverse-success mr-2">Support Manager</span>
</div>
<div class="wrapper d-flex align-items-start pt-3">
His interests include all things programming, and Pokemon.
He enjoys a good tech tangent, gaming, and playing on his phone.
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-5">
<div class="card rounded shadow-none">
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="user-avatar mb-auto">
<img src="/static/assets/images/credits/lukas_cropped.png"
alt="profile image" class="profile-img img-lg rounded-circle">
</div>
<div class="wrapper">
<div class="wrapper d-flex align-items-center">
<h4 class="mb-0 font-weight-medium">ThatOneLukas</h4>
</div>
<div class="wrapper d-flex align-items-center font-weight-medium text-muted">
<i class="mdi mdi-map-marker-outline mr-2"></i>
<p class="mb-0 text-muted">Helsinki, FI</p>
</div>
</div>
</div>
<div class="col-md-8">
<div class="wrapper d-flex align-items-start">
<span class="btn btn-sm btn-info mr-2">Staff</span>
<span class="btn btn-sm btn-primary mr-2">Developer</span>
</div>
<div class="wrapper d-flex align-items-start pt-3">
His interests include programming, gaming, and electronics.
He likes gaming, programming, messing around with electronics, and time with his family.
</div> </div>
</div> </div>
</div> </div>
@ -415,9 +218,9 @@
</div> </div>
</div> </div>
</div> </div>
{% end %}
</div> <!-- end user row--> </div> <!-- end user row-->
</div> </div>
</div> </div>
</div> </div>
@ -438,31 +241,22 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for pat in data["patreons"] %}
<tr> <tr>
<td>Richard B</td> <td>{{ pat["name"] }}</td>
<td> <td>
{% if pat["level"] == "substainer" %}
<span class="btn btn-sm btn-info mr-2">Substainer</span> <span class="btn btn-sm btn-info mr-2">Substainer</span>
</td> {% elif pat["level"] == "advocate" %}
</tr>
<tr>
<td>John C</td>
<td>
<span class="btn btn-sm btn-primary mr-2">Advocate</span> <span class="btn btn-sm btn-primary mr-2">Advocate</span>
{% elif pat["level"] == "supporter" %}
<span class="btn btn-sm btn-inverse-success mr-2">Supporter</span>
{% else %}
<span class="btn btn-sm btn-secondary mr-2">Other</span>
{% end %}
</td> </td>
</tr> </tr>
<tr> {% end %}
<td>Nicolas T</td>
<td>
<span class="btn btn-sm btn-info mr-2">Substainer</span>
</td>
</tr>
<tr>
<td>Lightkeeper</td>
<td>
<span class="btn btn-sm btn-info mr-2">Substainer</span>
</td>
</tr>
</tbody> </tbody>
</table> </table>
@ -483,27 +277,16 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for person in data['translations'] %}
<tr> <tr>
<td>Ultrablack</td> <td>{{ person }}</td>
<td> <td>
<span class="btn btn-sm btn-inverse-success mr-2">German</span> {% for language in data['translations'][person] %}
<span class="btn btn-sm btn-inverse-success mr-2">{{ language }}</span>
{% end %}
</td> </td>
</tr> </tr>
<tr> {% end %}
<td>Manu</td>
<td>
<span class="btn btn-sm btn-inverse-success mr-2">French</span>
</td>
</tr>
<tr>
<td>ptarrant</td>
<td>
<span class="btn btn-sm btn-inverse-success mr-2">Sarcasm</span>
</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -125,7 +125,7 @@
<td class="actions_serverlist"> <td class="actions_serverlist">
{% if server['stats'][0]['running'] %} {% if server['stats']['running'] %}
<a class="stop_button" data-id="{{server['server_data']['server_id']}}"> <i class="fas fa-stop"></i></a> &nbsp; <a class="stop_button" data-id="{{server['server_data']['server_id']}}"> <i class="fas fa-stop"></i></a> &nbsp;
<a class="restart_button" data-id="{{server['server_data']['server_id']}}"> <i class="fas fa-sync"></i></a> &nbsp; <a class="restart_button" data-id="{{server['server_data']['server_id']}}"> <i class="fas fa-sync"></i></a> &nbsp;
{% else %} {% else %}
@ -136,59 +136,59 @@
</td> </td>
<td> <td>
<div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="{{server['stats'][0]['cpu']}}"> <div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="{{server['stats']['cpu']}}">
<div class="progress-bar <div class="progress-bar
{% if server['stats'][0]['cpu'] <= 33 %} {% if server['stats']['cpu'] <= 33 %}
bg-success bg-success
{% elif 34 <= server['stats'][0]['cpu'] <= 66 %} {% elif 34 <= server['stats']['cpu'] <= 66 %}
bg-warning bg-warning
{% else %} {% else %}
bg-danger bg-danger
{% end %} {% end %}
" role="progressbar" style="width: {{server['stats'][0]['cpu']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div> " role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div> </div>
{{server['stats'][0]['cpu']}}% {{server['stats']['cpu']}}%
</td> </td>
<td> <td>
<div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="{{server['stats'][0]['mem']}}"> <div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="{{server['stats']['mem']}}">
<div class="progress-bar <div class="progress-bar
{% if server['stats'][0]['mem_percent'] <= 33 %} {% if server['stats']['mem_percent'] <= 33 %}
bg-success bg-success
{% elif 34 <= server['stats'][0]['mem_percent'] <= 66 %} {% elif 34 <= server['stats']['mem_percent'] <= 66 %}
bg-warning bg-warning
{% else %} {% else %}
bg-danger bg-danger
{% end %} {% end %}
" role="progressbar" style="width: {{server['stats'][0]['mem_percent']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div> " role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div> </div>
{{server['stats'][0]['mem_percent']}}% - {{server['stats']['mem_percent']}}% -
{% if server['stats'][0]['mem'] == 0 %} {% if server['stats']['mem'] == 0 %}
0 MB 0 MB
{% else %} {% else %}
{{server['stats'][0]['mem']}} {{server['stats']['mem']}}
{% end %} {% end %}
</td> </td>
<td> <td>
{{ server['stats'][0]['world_name'] }} : {{ server['stats'][0]['world_size'] }} {{ server['stats']['world_name'] }} : {{ server['stats']['world_size'] }}
</td> </td>
<td> <td>
{% if server['stats'][0]['int_ping_results'] %} {% if server['stats']['int_ping_results'] %}
{{ server['stats'][0]['online'] }} / {{ server['stats'][0]['max'] }} Max<br /> {{ server['stats']['online'] }} / {{ server['stats']['max'] }} Max<br />
{% if server['stats'][0]['desc'] != 'False' %} {% if server['stats']['desc'] != 'False' %}
{{ server['stats'][0]['desc'] }} <br /> {{ server['stats']['desc'] }} <br />
{% end %} {% end %}
{% if server['stats'][0]['version'] != 'False' %} {% if server['stats']['version'] != 'False' %}
{{ server['stats'][0]['version'] }} {{ server['stats']['version'] }}
{% end %} {% end %}
{% end %} {% end %}
</td> </td>
<td> <td>
{% if server['stats'][0]['running'] %} {% if server['stats']['running'] %}
<i class="fas fa-thumbs-up"></i> <span class="text-success">Online</span> <i class="fas fa-thumbs-up"></i> <span class="text-success">Online</span>
{% else %} {% else %}
<i class="fas fa-thumbs-down"></i> <span class="text-danger">Offline</span> <i class="fas fa-thumbs-down"></i> <span class="text-danger">Offline</span>

View File

@ -4,9 +4,9 @@
<div class="card-body pt-3 pb-3"> <div class="card-body pt-3 pb-3">
<div class="row"> <div class="row">
<div class="col-sm-3 mr-2"> <div class="col-sm-3 mr-2">
{% if data['server_stats'][0]['running'] %} {% if data['server_stats']['running'] %}
<b>Server Status:</b> <span class="text-success">Online</span><br /> <b>Server Status:</b> <span class="text-success">Online</span><br />
<b>Server Started:</b> <span id="started">{{ data['server_stats'][0]['started'] }} (Server Time)</span><br /> <b>Server Started:</b> <span id="started">{{ data['server_stats']['started'] }} (Server Time)</span><br />
<b>Server Uptime:</b> <span id="uptime">Error Calculating</span> <b>Server Uptime:</b> <span id="uptime">Error Calculating</span>
{% else %} {% else %}
<b>Server Status:</b> <span class="text-danger">Offline</span><br /> <b>Server Status:</b> <span class="text-danger">Offline</span><br />
@ -16,19 +16,19 @@
</div> </div>
<div class="col-sm-3 mr-2"> <div class="col-sm-3 mr-2">
<b>CPU:</b> {{ data['server_stats'][0]['cpu'] }}% <br /> <b>CPU:</b> {{ data['server_stats']['cpu'] }}% <br />
<b>Mem:</b> {{ data['server_stats'][0]['mem'] }} <br /> <b>Mem:</b> {{ data['server_stats']['mem'] }} <br />
{% if data['server_stats'][0]['int_ping_results'] %} {% if data['server_stats']['int_ping_results'] %}
<b>Players:</b> {{ data['server_stats'][0]['online'] }} / {{ data['server_stats'][0]['max'] }}<br /> <b>Players:</b> {{ data['server_stats']['online'] }} / {{ data['server_stats']['max'] }}<br />
{% else %} {% else %}
<b>Players:</b> 0/0<br /> <b>Players:</b> 0/0<br />
{% end %} {% end %}
</div> </div>
<div class="col-sm-3 mr-2"> <div class="col-sm-3 mr-2">
{% if data['server_stats'][0]['version'] != 'False' %} {% if data['server_stats']['version'] != 'False' %}
<b>Server:</b> {{ data['server_stats'][0]['version'] }} <br /> <b>Server:</b> {{ data['server_stats']['version'] }} <br />
<b>Desc:</b> {{ data['server_stats'][0]['desc'] }} <br /> <b>Desc:</b> {{ data['server_stats']['desc'] }} <br />
{% else %} {% else %}
<b>Server:</b> Unable To Connect <br /> <b>Server:</b> Unable To Connect <br />
<b>Desc:</b> Unable To Connect <br /> <b>Desc:</b> Unable To Connect <br />
@ -86,9 +86,9 @@
let startedLocal; let startedLocal;
if (started != null) { if (started != null) {
console.log('88', '{{ data['server_stats'][0]['started'] }}'); console.log('88', '{{ data['server_stats']['started'] }}');
{% if data['server_stats'][0]['started'] != 'False' %} {% if data['server_stats']['started'] != 'False' %}
startedUTC = '{{ (datetime.datetime.strptime(data['server_stats'][0]['started'], '%Y-%m-%d %H:%M:%S') - datetime.timedelta(seconds=-time.timezone)).strftime('%Y-%m-%d %H:%M:%S') }}'; startedUTC = '{{ (datetime.datetime.strptime(data['server_stats']['started'], '%Y-%m-%d %H:%M:%S') - datetime.timedelta(seconds=-time.timezone)).strftime('%Y-%m-%d %H:%M:%S') }}';
{% end %} {% end %}
console.log('utc', startedUTC); console.log('utc', startedUTC);
startedUTC = moment.utc(startedUTC, 'YYYY-MM-DD HH:mm:ss'); startedUTC = moment.utc(startedUTC, 'YYYY-MM-DD HH:mm:ss');
@ -105,7 +105,7 @@
} }
let nowServerTime = '{{ data['time'] }}'; let nowServerTime = '{{ data['time'] }}';
let startedServerTime = '{{ data['server_stats'][0]['started'] }}'; let startedServerTime = '{{ data['server_stats']['started'] }}';
if (uptime != null && started != null) { if (uptime != null && started != null) {

View File

@ -15,9 +15,9 @@
<div class="col-12"> <div class="col-12">
<div class="page-header"> <div class="page-header">
<h4 class="page-title"> <h4 class="page-title">
Server Details - {{ data['server_stats'][0]['server_id']['server_name'] }} Server Details - {{ data['server_stats']['server_id']['server_name'] }}
<br /> <br />
<small>UUID: {{ data['server_stats'][0]['server_id']['server_uuid'] }}</small> <small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
</h4> </h4>
</div> </div>
</div> </div>
@ -34,31 +34,31 @@
<div class="card-body pt-0"> <div class="card-body pt-0">
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist"> <ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Terminal</a> <i class="fas fa-file-signature"></i>Terminal</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Logs</a> <i class="fas fa-file-signature"></i>Logs</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false">
<i class="fas fa-clock"></i>Schedule</a> <i class="fas fa-clock"></i>Schedule</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
<i class="fas fa-save"></i>Backup</a> <i class="fas fa-save"></i>Backup</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
<i class="fas fa-folder-tree"></i>Files</a> <i class="fas fa-folder-tree"></i>Files</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="true"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="true">
<i class="fas fa-cogs"></i>Config</a> <i class="fas fa-cogs"></i>Config</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true"> <a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true">
<i class="fas fa-users"></i>Player Controls</a> <i class="fas fa-users"></i>Player Controls</a>
</li> </li>

View File

@ -0,0 +1,247 @@
{% extends ../base.html %}
{% block meta %}
<!-- <meta http-equiv="refresh" content="60">-->
{% end %}
{% block title %}Crafty Controller - Server Details{% end %}
{% block content %}
<div class="content-wrapper">
<!-- Page Title Header Starts-->
<div class="row page-title-header">
<div class="col-12">
<div class="page-header">
<h4 class="page-title">
Server Details - {{ data['server_stats']['server_id']['server_name'] }}
<br />
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
</h4>
</div>
</div>
</div>
<!-- Page Title Header Ends-->
{% include "parts/details_stats.html %}
<div class="row">
<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" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Terminal</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Logs</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false">
<i class="fas fa-clock"></i>Schedule</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="true">
<i class="fas fa-save"></i>Backup</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
<i class="fas fa-folder-tree"></i>Files</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="false">
<i class="fas fa-cogs"></i>Config</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="false">
<i class="fas fa-users"></i>Player Controls</a>
</li>
</ul>
<div class="row">
<div class="col-md-6 col-sm-12">
<form class="forms-sample" method="post" action="/panel/server_backup">
{% raw xsrf_form_html() %}
<input type="hidden" name="id" value="{{ data['server_stats']['server_id']['server_id'] }}">
<input type="hidden" name="subpage" value="backup">
<div class="form-group">
<a href="/panel/backup_now?id={{ data['server_stats']['server_id']['server_id'] }}" class="btn btn-primary" onclick="backup_started()">Backup Now!</a>
</div>
<div class="form-group">
<label for="server_name">Storage Location <small class="text-muted ml-1"> - Where do you want to store backups?</small> </label>
<input type="text" class="form-control" name="backup_path" id="backup_path" value="{{ data['server_stats']['server_id']['backup_path'] }}" placeholder="Backup Path" >
</div>
<div class="form-group">
<label for="server_path">Max Backups <small class="text-muted ml-1"> - Crafty will not store more than n backups, deleting the oldest (enter 0 to keep all)</small> </label>
<input type="text" class="form-control" name="max_backups" id="max_backups" value="{{ data['backup_config']['max_backups'] }}" placeholder="Max Backups" >
</div>
<div class="form-group">
<label for="superuser" class="form-check-label ml-4 mb-4">
{% if data['backup_config']['auto_enabled'] %}
<input type="checkbox" class="form-check-input" id="auto_enabled" name="auto_enabled" checked="" value="1" >Auto-backup at 12:00 AM?
{% else %}
<input type="checkbox" class="form-check-input" id="auto_enabled" name="auto_enabled" value="1" >Auto-backup at 12:00 AM?
{% end %}
</label>
</div>
<button type="submit" class="btn btn-success mr-2">Save</button>
<button type="reset" class="btn btn-light">Cancel</button>
</form>
</div>
<div class="col-md-6 col-sm-12">
<div class="card">
<div class="card-body">
<h4 class="card-title">Current Backups</h4>
</div>
</div>
<div class="text-center">
<table class="table table-responsive dataTable" id="backup_table">
<thead>
<tr>
<th width="10%">Download</th>
<th>Path</th>
<th width="20%">Size</th>
<th width="10%">Delete</th>
</tr>
</thead>
<tbody>
{% for backup in data['backup_list'] %}
<tr>
<td>
<a href="/panel/download_backup?file={{ backup['path'] }}&id={{ data['server_stats']['server_id']['server_id'] }}" class="btn btn-primary">
<i class="fas fa-download" aria-hidden="true"></i>
Download
</a>
</td>
<td>{{ backup['path'] }}</td>
<td>{{ backup['size'] }}</td>
<td>
<button data-file="{{ backup['path'] }}" class="btn btn-danger del_button">
<i class="fas fa-trash" aria-hidden="true"></i>
Delete
</button>
</td>
</tr>
{% end %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- content-wrapper ends -->
{% end %}
{% block js %}
<script>
//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;
}
function backup_started(time='5-10') {
bootbox.alert({
message: "A backup task has been started.",
backdrop: true
});
}
function del_backup(filename, id){
var token = getCookie("_xsrf")
data_to_send = { file_name :filename}
console.log('Sending Command to delete file: ' + filename)
$.ajax({
type: "POST",
headers: {'X-XSRFToken': token},
url: '/ajax/del_file?server_id='+id,
data: data_to_send,
success: function(data){
location.reload();
},
});
}
$( document ).ready(function() {
console.log( "ready!" );
$("#backup_config_box").hide();
$("#backup_save_note").hide();
$("#show_config").click(function () {
$("#backup_config_box").toggle();
$('#backup_button').hide();
$('#backup_save_note').show();
$('#backup_data').hide();
});
$('#backup_table').DataTable({
"order": [[ 1, "desc" ]],
"paging":true,
"lengthChange": false,
"searching": true,
"ordering": true,
"info": true,
"autoWidth": false,
"responsive": true,
});
$( ".del_button" ).click(function() {
var file_to_del = $(this).data("file");
console.log("file to delete is" + file_to_del);
bootbox.confirm({
title: "Destroy backup " + file_to_del + "?",
message: "Do you want to delete this file? This cannot be undone.",
buttons: {
cancel: {
label: '<i class="fas fa-times"></i> Cancel'
},
confirm: {
label: '<i class="fas fa-check"></i> Confirm'
}
},
callback: function (result) {
console.log(result);
if (result == true) {
del_backup(file_to_del, {{ data['server_stats']['server_id']['server_id'] }} );
}
}
});
});
});
</script>
{% end %}

View File

@ -15,9 +15,9 @@
<div class="col-12"> <div class="col-12">
<div class="page-header"> <div class="page-header">
<h4 class="page-title"> <h4 class="page-title">
Server Details - {{ data['server_stats'][0]['server_id']['server_name'] }} Server Details - {{ data['server_stats']['server_id']['server_name'] }}
<br /> <br />
<small>UUID: {{ data['server_stats'][0]['server_id']['server_uuid'] }}</small> <small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
</h4> </h4>
</div> </div>
</div> </div>
@ -34,31 +34,31 @@
<div class="card-body pt-0"> <div class="card-body pt-0">
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist"> <ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Terminal</a> <i class="fas fa-file-signature"></i>Terminal</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Logs</a> <i class="fas fa-file-signature"></i>Logs</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false">
<i class="fas fa-clock"></i>Schedule</a> <i class="fas fa-clock"></i>Schedule</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
<i class="fas fa-save"></i>Backup</a> <i class="fas fa-save"></i>Backup</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
<i class="fas fa-folder-tree"></i>Files</a> <i class="fas fa-folder-tree"></i>Files</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="true"> <a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="true">
<i class="fas fa-cogs"></i>Config</a> <i class="fas fa-cogs"></i>Config</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true">
<i class="fas fa-users"></i>Player Controls</a> <i class="fas fa-users"></i>Player Controls</a>
</li> </li>
@ -68,62 +68,62 @@
<div class="col-md-6 col-sm-12"> <div class="col-md-6 col-sm-12">
<form class="forms-sample" method="post" action="/panel/server_detail"> <form class="forms-sample" method="post" action="/panel/server_detail">
{% raw xsrf_form_html() %} {% raw xsrf_form_html() %}
<input type="hidden" name="id" value="{{ data['server_stats'][0]['server_id']['server_id'] }}"> <input type="hidden" name="id" value="{{ data['server_stats']['server_id']['server_id'] }}">
<input type="hidden" name="subpage" value="config"> <input type="hidden" name="subpage" value="config">
<div class="form-group"> <div class="form-group">
<label for="server_name">Server Name <small class="text-muted ml-1"> - What you wish to call this server</small> </label> <label for="server_name">Server Name <small class="text-muted ml-1"> - What you wish to call this server</small> </label>
<input type="text" class="form-control" name="server_name" id="server_name" value="{{ data['server_stats'][0]['server_id']['server_name'] }}" placeholder="Server Name" > <input type="text" class="form-control" name="server_name" id="server_name" value="{{ data['server_stats']['server_id']['server_name'] }}" placeholder="Server Name" >
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="server_path">Server Path <small class="text-muted ml-1"> - Absolute full path (not including executable)</small> </label> <label for="server_path">Server Path <small class="text-muted ml-1"> - Absolute full path (not including executable)</small> </label>
<input type="text" class="form-control" name="server_path" id="server_path" value="{{ data['server_stats'][0]['server_id']['path'] }}" placeholder="Server Path" > <input type="text" class="form-control" name="server_path" id="server_path" value="{{ data['server_stats']['server_id']['path'] }}" placeholder="Server Path" >
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="log_path">Server Log Location <small class="text-muted ml-1"> - Absolute full path to the log file</small> </label> <label for="log_path">Server Log Location <small class="text-muted ml-1"> - Absolute full path to the log file</small> </label>
<input type="text" class="form-control" name="log_path" id="log_path" value="{{ data['server_stats'][0]['server_id']['log_path'] }}" placeholder="Server Log" > <input type="text" class="form-control" name="log_path" id="log_path" value="{{ data['server_stats']['server_id']['log_path'] }}" placeholder="Server Log" >
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="executable">Server Executable <small class="text-muted ml-1"> - Just the executable file</small> </label> <label for="executable">Server Executable <small class="text-muted ml-1"> - Just the executable file</small> </label>
<input type="text" class="form-control" name="executable" id="executable" value="{{ data['server_stats'][0]['server_id']['executable'] }}" placeholder="Server Executable" > <input type="text" class="form-control" name="executable" id="executable" value="{{ data['server_stats']['server_id']['executable'] }}" placeholder="Server Executable" >
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="execution_command">Server Execution Command <small class="text-muted ml-1"> - What will be launched in a hidden terminal</small> </label> <label for="execution_command">Server Execution Command <small class="text-muted ml-1"> - What will be launched in a hidden terminal</small> </label>
<input type="text" class="form-control" name="execution_command" id="execution_command" value="{{ data['server_stats'][0]['server_id']['execution_command'] }}" placeholder="Server Execution Command" > <input type="text" class="form-control" name="execution_command" id="execution_command" value="{{ data['server_stats']['server_id']['execution_command'] }}" placeholder="Server Execution Command" >
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="stop_command">Server Stop Command <small class="text-muted ml-1"> - Command to send the program to stop it</small> </label> <label for="stop_command">Server Stop Command <small class="text-muted ml-1"> - Command to send the program to stop it</small> </label>
<input type="text" class="form-control" name="stop_command" id="stop_command" value="{{ data['server_stats'][0]['server_id']['stop_command'] }}" placeholder="Server Stop Command" > <input type="text" class="form-control" name="stop_command" id="stop_command" value="{{ data['server_stats']['server_id']['stop_command'] }}" placeholder="Server Stop Command" >
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="auto_start_delay">Server Autostart Delay <small class="text-muted ml-1"> - Delay before auto starting (if enabled below)</small> </label> <label for="auto_start_delay">Server Autostart Delay <small class="text-muted ml-1"> - Delay before auto starting (if enabled below)</small> </label>
<input type="number" class="form-control" name="auto_start_delay" id="auto_start_delay" value="{{ data['server_stats'][0]['server_id']['auto_start_delay'] }}" step="1" max="999" min="10" > <input type="number" class="form-control" name="auto_start_delay" id="auto_start_delay" value="{{ data['server_stats']['server_id']['auto_start_delay'] }}" step="1" max="999" min="10" >
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="server_ip">Server IP <small class="text-muted ml-1"> - IP Crafty should connect to for stats (Try a real ip instead of 127.0.0.1 if you have issues)</small> </label> <label for="server_ip">Server IP <small class="text-muted ml-1"> - IP Crafty should connect to for stats (Try a real ip instead of 127.0.0.1 if you have issues)</small> </label>
<input type="text" class="form-control" name="server_ip" id="server_ip" value="{{ data['server_stats'][0]['server_id']['server_ip'] }}"> <input type="text" class="form-control" name="server_ip" id="server_ip" value="{{ data['server_stats']['server_id']['server_ip'] }}">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="server_port">Server Port <small class="text-muted ml-1"> - Port Crafty should connect to for stats</small> </label> <label for="server_port">Server Port <small class="text-muted ml-1"> - Port Crafty should connect to for stats</small> </label>
<input type="number" class="form-control" name="server_port" id="server_port" value="{{ data['server_stats'][0]['server_id']['server_port'] }}" step="1" max="65566" min="1" > <input type="number" class="form-control" name="server_port" id="server_port" value="{{ data['server_stats']['server_id']['server_port'] }}" step="1" max="65566" min="1" >
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="logs_delete_after">Remove Old Logs After <small class="text-muted ml-1"> - How many days will a log file has to be old to get deleted (0 is off)</small> </label> <label for="logs_delete_after">Remove Old Logs After <small class="text-muted ml-1"> - How many days will a log file has to be old to get deleted (0 is off)</small> </label>
<input type="number" class="form-control" name="logs_delete_after" id="logs_delete_after" value="{{ data['server_stats'][0]['server_id']['logs_delete_after'] }}" step="1" max="365" min="0" > <input type="number" class="form-control" name="logs_delete_after" id="logs_delete_after" value="{{ data['server_stats']['server_id']['logs_delete_after'] }}" step="1" max="365" min="0" >
</div> </div>
<div class="form-check-flat"> <div class="form-check-flat">
<label for="auto_start" class="form-check-label ml-4 mb-4"> <label for="auto_start" class="form-check-label ml-4 mb-4">
{% if data['server_stats'][0]['server_id']['auto_start'] %} {% if data['server_stats']['server_id']['auto_start'] %}
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" checked="" value="1">Server Auto Start <input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" checked="" value="1">Server Auto Start
{% else %} {% else %}
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" value="1">Server Auto Start <input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" value="1">Server Auto Start
@ -131,7 +131,7 @@
</label> </label>
<label for="crash_detection" class="form-check-label ml-4 mb-4"> <label for="crash_detection" class="form-check-label ml-4 mb-4">
{% if data['server_stats'][0]['server_id']['crash_detection'] %} {% if data['server_stats']['server_id']['crash_detection'] %}
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection" checked="" value="1">Server Crash Detection <input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection" checked="" value="1">Server Crash Detection
{% else %} {% else %}
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection" value="1" >Server Crash Detection <input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection" value="1" >Server Crash Detection
@ -170,11 +170,11 @@
</div> </div>
</div> </div>
<div class="text-center"> <div class="text-center">
{% if data['server_stats'][0]['running'] %} {% if data['server_stats']['running'] %}
<a class="btn btn-sm btn-danger disabled">Delete Server</a><br /> <a class="btn btn-sm btn-danger disabled">Delete Server</a><br />
<small>Please stop the server before deleting it</small> <small>Please stop the server before deleting it</small>
{% else %} {% else %}
<a href="/panel/remove_server?id={{ data['server_stats'][0]['server_id']['server_id'] }}" class="btn btn-sm btn-danger">Delete Server</a> <a href="/panel/remove_server?id={{ data['server_stats']['server_id']['server_id'] }}" class="btn btn-sm btn-danger">Delete Server</a>
{% end %} {% end %}
</div> </div>

View File

@ -15,9 +15,9 @@
<div class="col-12"> <div class="col-12">
<div class="page-header"> <div class="page-header">
<h4 class="page-title"> <h4 class="page-title">
Server Details - {{ data['server_stats'][0]['server_id']['server_name'] }} Server Details - {{ data['server_stats']['server_id']['server_name'] }}
<br /> <br />
<small>UUID: {{ data['server_stats'][0]['server_id']['server_uuid'] }}</small> <small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
</h4> </h4>
</div> </div>
</div> </div>
@ -34,31 +34,31 @@
<div class="card-body pt-0"> <div class="card-body pt-0">
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist"> <ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Terminal</a> <i class="fas fa-file-signature"></i>Terminal</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Logs</a> <i class="fas fa-file-signature"></i>Logs</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false">
<i class="fas fa-clock"></i>Schedule</a> <i class="fas fa-clock"></i>Schedule</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
<i class="fas fa-save"></i>Backup</a> <i class="fas fa-save"></i>Backup</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false"> <a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
<i class="fas fa-folder-tree"></i>Files</a> <i class="fas fa-folder-tree"></i>Files</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="true"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="true">
<i class="fas fa-cogs"></i>Config</a> <i class="fas fa-cogs"></i>Config</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true">
<i class="fas fa-users"></i>Player Controls</a> <i class="fas fa-users"></i>Player Controls</a>
</li> </li>
@ -329,7 +329,7 @@
setFileName(event.target.innerText); setFileName(event.target.innerText);
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: '/ajax/get_file?id={{ data['server_stats'][0]['server_id']['server_id'] }}&file_path=' + encodeURIComponent(filePath), url: '/ajax/get_file?id={{ data['server_stats']['server_id']['server_id'] }}&file_path=' + encodeURIComponent(filePath),
dataType: 'text', dataType: 'text',
success: function (data) { success: function (data) {
console.log('Got File Contents From Server'); console.log('Got File Contents From Server');
@ -393,7 +393,7 @@
$.ajax({ $.ajax({
type: "PUT", type: "PUT",
headers: {'X-XSRFToken': token}, headers: {'X-XSRFToken': token},
url: '/ajax/save_file?id={{ data['server_stats'][0]['server_id']['server_id'] }}', url: '/ajax/save_file?id={{ data['server_stats']['server_id']['server_id'] }}',
data: { data: {
file_contents: text, file_contents: text,
file_path: filePath file_path: filePath
@ -410,7 +410,7 @@
$.ajax({ $.ajax({
type: "POST", type: "POST",
headers: {'X-XSRFToken': token}, headers: {'X-XSRFToken': token},
url: '/ajax/create_file?id={{ data['server_stats'][0]['server_id']['server_id'] }}', url: '/ajax/create_file?id={{ data['server_stats']['server_id']['server_id'] }}',
data: { data: {
file_parent: parent, file_parent: parent,
file_name: name file_name: name
@ -428,7 +428,7 @@
$.ajax({ $.ajax({
type: "POST", type: "POST",
headers: {'X-XSRFToken': token}, headers: {'X-XSRFToken': token},
url: '/ajax/create_dir?id={{ data['server_stats'][0]['server_id']['server_id'] }}', url: '/ajax/create_dir?id={{ data['server_stats']['server_id']['server_id'] }}',
data: { data: {
dir_parent: parent, dir_parent: parent,
dir_name: name dir_name: name
@ -446,7 +446,7 @@
$.ajax({ $.ajax({
type: "PUT", type: "PUT",
headers: {'X-XSRFToken': token}, headers: {'X-XSRFToken': token},
url: '/ajax/rename_item?id={{ data['server_stats'][0]['server_id']['server_id'] }}', url: '/ajax/rename_item?id={{ data['server_stats']['server_id']['server_id'] }}',
data: { data: {
item_path: path, item_path: path,
new_item_name: name new_item_name: name
@ -465,7 +465,7 @@
$.ajax({ $.ajax({
type: "DELETE", type: "DELETE",
headers: {'X-XSRFToken': token}, headers: {'X-XSRFToken': token},
url: '/ajax/del_file?id={{ data['server_stats'][0]['server_id']['server_id'] }}', url: '/ajax/del_file?id={{ data['server_stats']['server_id']['server_id'] }}',
data: { data: {
file_path: path file_path: path
}, },
@ -482,7 +482,7 @@
$.ajax({ $.ajax({
type: "DELETE", type: "DELETE",
headers: {'X-XSRFToken': token}, headers: {'X-XSRFToken': token},
url: '/ajax/del_dir?id={{ data['server_stats'][0]['server_id']['server_id'] }}', url: '/ajax/del_dir?id={{ data['server_stats']['server_id']['server_id'] }}',
data: { data: {
dir_path: path dir_path: path
}, },
@ -497,7 +497,7 @@
function getTreeView() { function getTreeView() {
$.ajax({ $.ajax({
type: "GET", type: "GET",
url: '/ajax/get_tree?id={{ data['server_stats'][0]['server_id']['server_id'] }}', url: '/ajax/get_tree?id={{ data['server_stats']['server_id']['server_id'] }}',
dataType: 'text', dataType: 'text',
success: function(data){ success: function(data){
console.log("got response:"); console.log("got response:");

View File

@ -15,9 +15,9 @@
<div class="col-12"> <div class="col-12">
<div class="page-header"> <div class="page-header">
<h4 class="page-title"> <h4 class="page-title">
Server Details - {{ data['server_stats'][0]['server_id']['server_name'] }} Server Details - {{ data['server_stats']['server_id']['server_name'] }}
<br /> <br />
<small>UUID: {{ data['server_stats'][0]['server_id']['server_uuid'] }}</small> <small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
</h4> </h4>
</div> </div>
</div> </div>
@ -35,31 +35,31 @@
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist"> <ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false">
<i class="fas fa-terminal"></i>Terminal</a> <i class="fas fa-terminal"></i>Terminal</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="true"> <a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="true">
<i class="fas fa-file-signature"></i>Logs</a> <i class="fas fa-file-signature"></i>Logs</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false">
<i class="fas fa-clock"></i>Schedule</a> <i class="fas fa-clock"></i>Schedule</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
<i class="fas fa-save"></i>Backup</a> <i class="fas fa-save"></i>Backup</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
<i class="fas fa-folder-tree"></i>Files</a> <i class="fas fa-folder-tree"></i>Files</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="false">
<i class="fas fa-cogs"></i>Config</a> <i class="fas fa-cogs"></i>Config</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true">
<i class="fas fa-users"></i>Player Controls</a> <i class="fas fa-users"></i>Player Controls</a>
</li> </li>
@ -91,7 +91,7 @@
if( !$("#stop_scroll").is(':checked')){ if( !$("#stop_scroll").is(':checked')){
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: '/ajax/server_log?id={{ data['server_stats'][0]['server_id']['server_id'] }}&full=1', url: '/ajax/server_log?id={{ data['server_stats']['server_id']['server_id'] }}&full=1',
dataType: 'text', dataType: 'text',
success: function (data) { success: function (data) {
console.log('Got Log From Server') console.log('Got Log From Server')

View File

@ -15,9 +15,9 @@
<div class="col-12"> <div class="col-12">
<div class="page-header"> <div class="page-header">
<h4 class="page-title"> <h4 class="page-title">
Server Details - {{ data['server_stats'][0]['server_id']['server_name'] }} Server Details - {{ data['server_stats']['server_id']['server_name'] }}
<br /> <br />
<small>UUID: {{ data['server_stats'][0]['server_id']['server_uuid'] }}</small> <small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
</h4> </h4>
</div> </div>
</div> </div>
@ -35,31 +35,31 @@
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist"> <ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item term-nav-item"> <li class="nav-item term-nav-item">
<a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="true"> <a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="true">
<i class="fas fa-file-signature"></i>Terminal</a> <i class="fas fa-file-signature"></i>Terminal</a>
</li> </li>
<li class="nav-item term-nav-item"> <li class="nav-item term-nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Logs</a> <i class="fas fa-file-signature"></i>Logs</a>
</li> </li>
<li class="nav-item term-nav-item"> <li class="nav-item term-nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false">
<i class="fas fa-clock"></i>Schedule</a> <i class="fas fa-clock"></i>Schedule</a>
</li> </li>
<li class="nav-item term-nav-item"> <li class="nav-item term-nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
<i class="fas fa-save"></i>Backup</a> <i class="fas fa-save"></i>Backup</a>
</li> </li>
<li class="nav-item term-nav-item"> <li class="nav-item term-nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
<i class="fas fa-folder-tree"></i>Files</a> <i class="fas fa-folder-tree"></i>Files</a>
</li> </li>
<li class="nav-item term-nav-item"> <li class="nav-item term-nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="false">
<i class="fas fa-cogs"></i>Config</a> <i class="fas fa-cogs"></i>Config</a>
</li> </li>
<li class="nav-item term-nav-item"> <li class="nav-item term-nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true"> <a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true">
<i class="fas fa-users"></i>Player Controls</a> <i class="fas fa-users"></i>Player Controls</a>
</li> </li>
<li class="nav-item term-nav-item"> <li class="nav-item term-nav-item">
@ -128,7 +128,7 @@
// Convert running to lower case (example: 'True' converts to 'true') and // Convert running to lower case (example: 'True' converts to 'true') and
// then to boolean via JSON.parse() // then to boolean via JSON.parse()
let online = JSON.parse('{{ data['server_stats'][0]['running'] }}'.toLowerCase()); let online = JSON.parse('{{ data['server_stats']['running'] }}'.toLowerCase());
let startBtn = document.querySelector('#start-btn'); let startBtn = document.querySelector('#start-btn');
let restartBtn = document.querySelector('#restart-btn'); let restartBtn = document.querySelector('#restart-btn');
@ -144,13 +144,13 @@
stopBtn.setAttribute('disabled', 'disabled'); stopBtn.setAttribute('disabled', 'disabled');
} }
let server_id = '{{ data['server_stats'][0]['server_id']['server_id'] }}'; let server_id = '{{ data['server_stats']['server_id']['server_id'] }}';
function get_server_log(){ function get_server_log(){
if( !$("#stop_scroll").is(':checked')){ if( !$("#stop_scroll").is(':checked')){
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: '/ajax/server_log?id={{ data['server_stats'][0]['server_id']['server_id'] }}', url: '/ajax/server_log?id={{ data['server_stats']['server_id']['server_id'] }}',
dataType: 'text', dataType: 'text',
success: function (data) { success: function (data) {
console.log('Got Log From Server') console.log('Got Log From Server')
@ -216,7 +216,7 @@
$.ajax({ $.ajax({
type: "POST", type: "POST",
headers: {'X-XSRFToken': token}, headers: {'X-XSRFToken': token},
url: '/ajax/send_command?id={{ data['server_stats'][0]['server_id']['server_id'] }}', url: '/ajax/send_command?id={{ data['server_stats']['server_id']['server_id'] }}',
data: data_to_send, data: data_to_send,
success: function(data){ success: function(data){
console.log("got response:"); console.log("got response:");

37
main.py
View File

@ -10,8 +10,9 @@ from app.classes.shared.console import console
from app.classes.shared.helpers import helper from app.classes.shared.helpers import helper
from app.classes.shared.models import installer from app.classes.shared.models import installer
from app.classes.shared.controller import controller
from app.classes.shared.tasks import TasksManager from app.classes.shared.tasks import TasksManager
from app.classes.shared.controller import Controller
from app.classes.shared.cmd import MainPrompt from app.classes.shared.cmd import MainPrompt
@ -56,7 +57,6 @@ def setup_logging(debug=True):
""" Our Main Starter """ """ Our Main Starter """
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser("Crafty Controller - A Server Management System") parser = argparse.ArgumentParser("Crafty Controller - A Server Management System")
parser.add_argument('-i', '--ignore', parser.add_argument('-i', '--ignore',
@ -94,9 +94,14 @@ if __name__ == '__main__':
fresh_install = installer.is_fresh_install() fresh_install = installer.is_fresh_install()
if fresh_install: if fresh_install:
console.debug("Fresh install detected")
installer.create_tables() installer.create_tables()
installer.default_settings() installer.default_settings()
else:
console.debug("Existing install detected")
installer.check_schema_version()
<<<<<<< HEAD
# init servers # init servers
logger.info("Initializing all servers defined") logger.info("Initializing all servers defined")
console.info("Initializing all servers defined") console.info("Initializing all servers defined")
@ -107,15 +112,31 @@ if __name__ == '__main__':
# now the tables are created, we can load the tasks_manger # now the tables are created, we can load the tasks_manger
tasks_manager = TasksManager() tasks_manager = TasksManager()
=======
# now the tables are created, we can load the tasks_manger and server controller
controller = Controller()
tasks_manager = TasksManager(controller)
>>>>>>> backups-and-stuff
tasks_manager.start_webserver() tasks_manager.start_webserver()
tasks_manager.start_scheduler()
# slowing down reporting just for a 1/2 second so messages look cleaner # slowing down reporting just for a 1/2 second so messages look cleaner
time.sleep(.5) time.sleep(.5)
<<<<<<< HEAD
=======
# init servers
logger.info("Initializing all servers defined")
console.info("Initializing all servers defined")
controller.init_all_servers()
servers = controller.list_defined_servers()
>>>>>>> backups-and-stuff
# start stats logging # start stats logging
tasks_manager.start_stats_recording() tasks_manager.start_stats_recording()
# once the controller is up and stats are logging, we can kick off the scheduler officially
tasks_manager.start_scheduler()
# refresh our cache and schedule for every 12 hoursour cache refresh for serverjars.com # refresh our cache and schedule for every 12 hoursour cache refresh for serverjars.com
tasks_manager.serverjar_cache_refresher() tasks_manager.serverjar_cache_refresher()
@ -123,6 +144,7 @@ if __name__ == '__main__':
tasks_manager.start_main_kill_switch_watcher() tasks_manager.start_main_kill_switch_watcher()
Crafty = MainPrompt(tasks_manager) Crafty = MainPrompt(tasks_manager)
<<<<<<< HEAD
if not args.daemon: if not args.daemon:
Crafty.cmdloop() Crafty.cmdloop()
else: else:
@ -143,3 +165,12 @@ if __name__ == '__main__':
if tasks_manager.get_main_thread_run_status(): if tasks_manager.get_main_thread_run_status():
sys.exit(0) sys.exit(0)
time.sleep(1) time.sleep(1)
=======
Crafty.cmdloop()
# our main loop - eventually a shell
# while True:
# if tasks_manager.get_main_thread_run_status():
# sys.exit(0)
# time.sleep(1)
>>>>>>> backups-and-stuff