scheduler, version change, database work, controller init servers, etc etc

This commit is contained in:
Phillip Tarrant 2020-08-16 22:47:53 -04:00
parent 25affba2e6
commit ad541347af
15 changed files with 714 additions and 62 deletions

View File

@ -0,0 +1,138 @@
import os
import time
import logging
import sys
import yaml
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
from app.classes.shared.models import db_helper
from app.classes.minecraft.server import Server
from app.classes.minecraft.server_props import ServerProps
logger = logging.getLogger(__name__)
class Controller:
def __init__(self):
self.servers_list = []
def init_all_servers(self):
# if we have servers defined, let's destroy it and start over
if len(self.servers_list) > 0:
self.servers_list = []
servers = db_helper.get_all_defined_servers()
for s in servers:
settings_file = os.path.join(s['path'], 'server.properties')
settings = ServerProps(settings_file)
temp_server_dict = {
'server_id': s.get('server_id'),
'server_data_obj': s,
'server_obj': Server(),
'server_settings': settings.props
}
# setup the server, do the auto start and all that jazz
temp_server_dict['server_obj'].do_server_setup(s)
# add this temp object to the list of init servers
self.servers_list.append(temp_server_dict)
console.info("Loaded Server: ID {} | Name: {} | Autostart: {} | Delay: {} ".format(
s['server_id'],
s['server_name'],
s['auto_start'],
s['auto_start_delay']
))
def get_server_obj(self, server_id):
for s in self.servers_list:
if int(s['server_id']) == int(server_id):
return s['server_obj']
logger.warning("Unable to find server object for server id {}".format(server_id))
return False
@staticmethod
def list_defined_servers():
servers = db_helper.get_all_defined_servers()
def list_running_servers(self):
running_servers = []
if len(self.servers_list) > 0:
# for each server
for s in self.servers_list:
# is the server running?
srv_obj = s['server_obj']
running = srv_obj.check_running()
# if so, let's add a dictionary to the list of running servers
if running:
running_servers.append({
'id': srv_obj.server_id,
'name': srv_obj.name
})
return running_servers
def stop_all_servers(self):
servers = self.list_running_servers()
logger.info("Found {} running server(s)".format(len(servers)))
console.info("Found {} running server(s)".format(len(servers)))
logger.info("Stopping All Servers")
console.info("Stopping All Servers")
for s in servers:
logger.info("Stopping Server ID {} - {}".format(s['id'], s['name']))
console.info("Stopping Server ID {} - {}".format(s['id'], s['name']))
# get object
svr_obj = self.get_server_obj(s['id'])
running = svr_obj.check_running(True)
# issue the stop command
svr_obj.stop_threaded_server()
# while it's running, we wait
x = 0
while running:
logger.info("Server {} is still running - waiting 2s to see if it stops".format(s['name']))
console.info("Server {} is still running - waiting 2s to see if it stops".format(s['name']))
running = svr_obj.check_running()
# let's keep track of how long this is going on...
x = x + 1
# if we have been waiting more than 120 seconds. let's just kill the pid
if x >= 60:
logger.error("Server {} is taking way too long to stop. Killing this process".format(s['name']))
console.error("Server {} is taking way too long to stop. Killing this process".format(s['name']))
svr_obj.killpid(svr_obj.PID)
running = False
# if we killed the server, let's clean up the object
if not running:
svr_obj.cleanup_server_object()
time.sleep(2)
# let's wait 2 seconds to let everything flush out
time.sleep(2)
logger.info("All Servers Stopped")
console.info("All Servers Stopped")
controller = Controller()

View File

@ -0,0 +1,270 @@
import os
import re
import json
import time
import psutil
import pexpect
import datetime
import threading
import schedule
import logging.config
from pexpect.popen_spawn import PopenSpawn
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
logger = logging.getLogger(__name__)
class Server:
def __init__(self):
# holders for our process
self.process = None
self.line = False
self.PID = None
self.start_time = None
self.server_command = None
self.server_path = None
self.server_thread = None
self.settings = None
self.updating = False
self.server_id = None
self.name = None
self.is_crashed = False
self.restart_count = 0
def do_server_setup(self, server_data_obj):
logger.info('Creating Server object: {} | Server Name: {} | Auto Start: {}'.format(
server_data_obj['server_id'],
server_data_obj['server_name'],
server_data_obj['auto_start']
))
self.server_id = server_data_obj['server_id']
self.name = server_data_obj['server_name']
self.settings = server_data_obj
# build our server run command
self.setup_server_run_command()
if server_data_obj['auto_start']:
delay = int(self.settings['auto_start_delay'])
logger.info("Scheduling server {} to start in {} seconds".format(self.name, delay))
console.info("Scheduling server {} to start in {} seconds".format(self.name, delay))
schedule.every(delay).seconds.do(self.run_scheduled_server)
def run_scheduled_server(self):
console.info("Starting Minecraft server ID: {} - {}".format(self.server_id, self.name))
logger.info("Starting Minecraft server {}".format(self.server_id, self.name))
self.run_threaded_server()
# remove the scheduled job since it's ran
return schedule.CancelJob
def run_threaded_server(self):
# start the server
self.server_thread = threading.Thread(target=self.start_server, daemon=True)
self.server_thread.start()
def setup_server_run_command(self):
# configure the server
server_exec_path = self.settings['executable']
self.server_command = self.settings['execution_command']
self.server_path = self.settings['path']
# let's do some quick checking to make sure things actually exists
full_path = os.path.join(self.server_path, server_exec_path)
if not helper.check_file_exists(full_path):
logger.critical("Server executable path: {} does not seem to exist".format(full_path))
console.critical("Server executable path: {} does not seem to exist".format(full_path))
helper.do_exit()
if not helper.check_path_exits(self.server_path):
logger.critical("Server path: {} does not seem to exits".format(self.server_path))
console.critical("Server path: {} does not seem to exits".format(self.server_path))
helper.do_exit()
if not helper.check_writeable(self.server_path):
logger.critical("Unable to write/access {}".format(self.server_path))
console.warning("Unable to write/access {}".format(self.server_path))
helper.do_exit()
def start_server(self):
# fail safe in case we try to start something already running
if self.check_running():
logger.error("Server is already running - Cancelling Startup")
console.error("Server is already running - Cancelling Startup")
return False
logger.info("Launching Server {} with command {}".format(self.name, self.server_command))
console.info("Launching Server {} with command {}".format(self.name, self.server_command))
if os.name == "nt":
logger.info("Windows Detected - launching cmd")
self.server_command = self.server_command.replace('\\', '/')
logging.info("Opening CMD prompt")
self.process = pexpect.popen_spawn.PopenSpawn('cmd \r\n', timeout=None, encoding=None)
drive_letter = self.server_path[:1]
if drive_letter.lower() != "c":
logger.info("Server is not on the C drive, changing drive letter to {}:".format(drive_letter))
self.process.send("{}:\r\n".format(drive_letter))
logging.info("changing directories to {}".format(self.server_path.replace('\\', '/')))
self.process.send('cd {} \r\n'.format(self.server_path.replace('\\', '/')))
logging.info("Sending command {} to CMD".format(self.server_command))
self.process.send(self.server_command + "\r\n")
self.is_crashed = False
else:
logger.info("Linux Detected - launching Bash")
self.process = pexpect.popen_spawn.PopenSpawn('/bin/bash \n', timeout=None, encoding=None)
logger.info("Changing directory to {}".format(self.server_path))
self.process.send('cd {} \n'.format(self.server_path))
logger.info("Sending server start command: {} to shell".format(self.server_command))
self.process.send(self.server_command + '\n')
self.is_crashed = False
ts = time.time()
self.start_time = str(datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S'))
if psutil.pid_exists(self.process.pid):
parent = psutil.Process(self.process.pid)
time.sleep(.5)
children = parent.children(recursive=True)
for c in children:
self.PID = c.pid
logger.info("Server {} running with PID {}".format(self.name, self.PID))
console.info("Server {} running with PID {}".format(self.name, self.PID))
self.is_crashed = False
else:
logger.warning("Server PID {} died right after starting - is this a server config issue?".format(self.PID))
console.warning("Server PID {} died right after starting - is this a server config issue?".format(self.PID))
if self.settings['crash_detection']:
logger.info("Server {} has crash detection enabled - starting watcher task".format(self.name))
console.info("Server {} has crash detection enabled - starting watcher task".format(self.name))
# TODO: create crash detection watcher and such
# schedule.every(30).seconds.do(self.check_running).tag(self.name)
def stop_threaded_server(self):
self.stop_server()
if self.server_thread:
self.server_thread.join()
def stop_server(self):
if self.settings['stop_command_needed']:
self.send_command(self.settings['stop_command'])
else:
self.killpid(self.PID)
def cleanup_server_object(self):
self.process = None
self.PID = None
self.start_time = None
self.name = None
def check_running(self, shutting_down=False):
# if process is None, we never tried to start
if self.PID is None:
return False
try:
running = psutil.pid_exists(self.PID)
except Exception as e:
logger.error("Unable to find if server PID exists: {}".format(self.PID))
running = False
pass
if not running:
# did the server crash?
if not shutting_down:
# do we have crash detection turned on?
if self.settings['crash_detection']:
# if we haven't tried to restart more 3 or more times
if self.restart_count <= 3:
# start the server if needed
server_restarted = self.crash_detected(self.name)
if server_restarted:
# add to the restart count
self.restart_count = self.restart_count + 1
return False
# we have tried to restart 4 times...
elif self.restart_count == 4:
logger.warning("Server {} has been restarted {} times. It has crashed, not restarting.".format(
self.name, self.restart_count))
# set to 99 restart attempts so this elif is skipped next time. (no double logging)
self.restart_count = 99
self.is_crashed = True
return False
else:
self.is_crashed = True
return False
self.process = None
self.PID = None
self.name = None
return False
return True
def send_command(self, command):
if not self.check_running() and command.lower() != 'start':
logger.warning("Server not running, unable to send command \"{}\"".format(command))
return False
logger.debug("Sending command {} to server via pexpect".format(command))
# send it
self.process.send(command + '\n')
def crash_detected(self, name):
# the server crashed, or isn't found - so let's reset things.
logger.warning("The server {} seems to have vanished unexpectedly, did it crash?".format(name))
if self.settings['crash_detection']:
logger.info("The server {} has crashed and will be restarted. Restarting server".format(name))
self.run_threaded_server()
return True
else:
logger.info("The server {} has crashed, crash detection is disabled and it will not be restarted".format(name))
return False
def killpid(self, pid):
logger.info("Terminating PID {} and all child processes".format(pid))
process = psutil.Process(pid)
# for every sub process...
for proc in process.children(recursive=True):
# kill all the child processes - it sounds too wrong saying kill all the children (kevdagoat: lol!)
logger.info("Sending SIGKILL to PID {}".format(proc.name))
proc.kill()
# kill the main process we are after
logger.info('Sending SIGKILL to parent')
process.kill()
def get_start_time(self):
if self.check_running():
return self.start_time
else:
return False

View File

@ -0,0 +1,66 @@
import pprint
import os
class ServerProps:
def __init__(self, filepath):
self.filepath = filepath
self.props = self._parse()
def _parse(self):
"""Loads and parses the file speified in self.filepath"""
with open(self.filepath) as fp:
line = fp.readline()
d = {}
if os.path.exists(".header"):
os.remove(".header")
while line:
if '#' != line[0]:
s = line
s1 = s[:s.find('=')]
if '\n' in s:
s2 = s[s.find('=')+1:s.find('\\')]
else:
s2 = s[s.find('=')+1:]
d[s1] = s2
else:
with open(".header", "a+") as h:
h.write(line)
line = fp.readline()
return d
def print(self):
"""Prints the properties dictionary (using pprint)"""
pprint.pprint(self.props)
def get(self):
"""Returns the properties dictionary"""
return self.props
def update(self, key, val):
"""Updates property in the properties dictionary [ update("pvp", "true") ] and returns boolean condition"""
if key in self.props.keys():
self.props[key] = val
return True
else:
return False
def save(self):
"""Writes to the new file"""
with open(self.filepath, "a+") as f:
f.truncate(0)
with open(".header") as header:
line = header.readline()
while line:
f.write(line)
line = header.readline()
header.close()
for key, value in self.props.items():
f.write(key + "=" + value + "\n")
if os.path.exists(".header"):
os.remove(".header")
@staticmethod
def cleanup():
if os.path.exists(".header"):
os.remove(".header")

View File

@ -32,7 +32,7 @@ class Helpers:
self.session_file = os.path.join(self.root_dir, 'session.lock') self.session_file = os.path.join(self.root_dir, 'session.lock')
self.settings_file = os.path.join(self.root_dir, 'config.ini') self.settings_file = os.path.join(self.root_dir, 'config.ini')
self.webroot = os.path.join(self.root_dir, 'app', 'frontend') self.webroot = os.path.join(self.root_dir, 'app', 'frontend')
self.db_path = os.path.join(self.root_dir, 'commander.sqlite') self.db_path = os.path.join(self.root_dir, 'crafty.sqlite')
self.passhasher = PasswordHasher() self.passhasher = PasswordHasher()
self.exiting = False self.exiting = False
@ -74,6 +74,15 @@ class Helpers:
return version_data return version_data
def get_version_string(self):
version_data = self.get_version()
# set some defaults if we don't get version_data from our helper
version = "{}.{}.{}".format(version_data.get('major', '?'),
version_data.get('minor', '?'),
version_data.get('sub', '?'))
return str(version)
def do_exit(self): def do_exit(self):
exit_file = os.path.join(self.root_dir, 'exit.txt') exit_file = os.path.join(self.root_dir, 'exit.txt')
try: try:
@ -111,6 +120,7 @@ class Helpers:
def ensure_logging_setup(self): def ensure_logging_setup(self):
log_file = os.path.join(os.path.curdir, 'logs', 'commander.log') log_file = os.path.join(os.path.curdir, 'logs', 'commander.log')
session_log_file = os.path.join(os.path.curdir, 'logs', 'session.log')
logger.info("Checking app directory writable") logger.info("Checking app directory writable")
@ -136,7 +146,7 @@ class Helpers:
# del any old session.lock file as this is a new session # del any old session.lock file as this is a new session
try: try:
os.remove(self.session_file) os.remove(session_log_file)
except: except:
pass pass
@ -326,5 +336,11 @@ class Helpers:
""" """
return ''.join(random.choice(chars) for x in range(size)) return ''.join(random.choice(chars) for x in range(size))
@staticmethod
def is_os_windows():
if os.name == 'nt':
return True
else:
return False
helper = Helpers() helper = Helpers()

View File

@ -1,15 +1,18 @@
import os
import sys import sys
import logging import logging
import 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.minecraft.server_props import ServerProps
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
try: try:
from peewee import * from peewee import *
from playhouse.shortcuts import model_to_dict from playhouse.shortcuts import model_to_dict
import yaml
except ModuleNotFoundError as e: except ModuleNotFoundError as e:
logger.critical("Import Error: Unable to load {} module".format(e, e.name)) logger.critical("Import Error: Unable to load {} module".format(e, e.name))
@ -59,6 +62,23 @@ class Host_Stats(BaseModel):
table_name = "host_stats" table_name = "host_stats"
class Servers(BaseModel):
server_id = AutoField()
created = DateTimeField(default=datetime.datetime.now)
server_name = CharField(default="Server")
path = CharField(default="")
executable = CharField(default="")
log_path = CharField(default="")
execution_command = CharField(default="")
auto_start = BooleanField(default=0)
auto_start_delay = IntegerField(default=10)
crash_detection = BooleanField(default=0)
stop_command = CharField(default="stop")
class Meta:
table_name = "servers"
class Webhooks(BaseModel): class Webhooks(BaseModel):
id = AutoField() id = AutoField()
name = CharField(max_length=64, unique=True) name = CharField(max_length=64, unique=True)
@ -80,6 +100,7 @@ class Backups(BaseModel):
class Meta: class Meta:
table_name = 'backups' table_name = 'backups'
class db_builder: class db_builder:
@staticmethod @staticmethod
@ -89,7 +110,8 @@ class db_builder:
Backups, Backups,
Users, Users,
Host_Stats, Host_Stats,
Webhooks Webhooks,
Servers
]) ])
@staticmethod @staticmethod
@ -105,7 +127,25 @@ class db_builder:
def is_fresh_install(): def is_fresh_install():
if helper.check_file_exists(helper.db_path): if helper.check_file_exists(helper.db_path):
return False return False
return True return True
class db_shortcuts:
def return_rows(self, query):
rows = []
if query:
for s in query:
rows.append(model_to_dict(s))
else:
rows.append({})
return rows
def get_all_defined_servers(self):
query = Servers.select()
return self.return_rows(query)
installer = db_builder() installer = db_builder()
db_helper = db_shortcuts()

View File

@ -5,13 +5,20 @@ import time
import logging import logging
import threading import threading
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.minecraft import server_props
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
try:
import schedule
except ModuleNotFoundError as e:
logger.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)
class TasksManager: class TasksManager:
@ -22,6 +29,8 @@ class TasksManager:
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")
self.main_thread_exiting = False self.main_thread_exiting = False
self.schedule_thread = threading.Thread(target=self.scheduler_thread, daemon=True, name="scheduler")
def get_main_thread_run_status(self): def get_main_thread_run_status(self):
return self.main_thread_exiting return self.main_thread_exiting
@ -39,9 +48,12 @@ class TasksManager:
# commander.stop_all_servers() # commander.stop_all_servers()
logger.info("***** Crafty Shutting Down *****\n\n") logger.info("***** Crafty Shutting Down *****\n\n")
console.info("***** Crafty Shutting Down *****\n\n") console.info("***** Crafty Shutting Down *****\n\n")
try:
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'))
# ServerProps.cleanup() os.remove(os.path.join(helper.root_dir, '.header'))
except:
pass
self.main_thread_exiting = True self.main_thread_exiting = True
def start_webserver(self): def start_webserver(self):
@ -57,6 +69,16 @@ class TasksManager:
def stop_webserver(self): def stop_webserver(self):
self.tornado.stop_web_server() self.tornado.stop_web_server()
def start_scheduler(self):
logger.info("Launching Scheduler Thread...")
console.info("Launching Scheduler Thread...")
self.schedule_thread.start()
@staticmethod
def scheduler_thread():
while True:
schedule.run_pending()
time.sleep(1)

View File

@ -5,12 +5,13 @@ import tornado.escape
import bleach import bleach
from app.classes.shared.console import console from app.classes.shared.console import console
from app.classes.shared.models import Users 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.minecraft.controller import controller
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class PanelHandler(BaseHandler): class PanelHandler(BaseHandler):
@tornado.web.authenticated @tornado.web.authenticated
@ -25,6 +26,8 @@ class PanelHandler(BaseHandler):
'user_data': user_data 'user_data': user_data
} }
servers = controller.list_defined_servers()
if page == 'unauthorized': if page == 'unauthorized':
template = "panel/denied.html" template = "panel/denied.html"

View File

@ -40,7 +40,9 @@ class PublicHandler(BaseHandler):
self.clear_cookie("user") self.clear_cookie("user")
self.clear_cookie("user_data") self.clear_cookie("user_data")
# print(page) page_data = {
'version': helper.get_version_string()
}
error = bleach.clean(self.get_argument('error', "")) error = bleach.clean(self.get_argument('error', ""))
@ -51,19 +53,14 @@ class PublicHandler(BaseHandler):
# sensible defaults # sensible defaults
template = "public/404.html" template = "public/404.html"
page_data = "{}"
# if we have no page, let's go to login
if page is None:
self.redirect("public/login")
if page == "login": if page == "login":
template = "public/login.html" template = "public/login.html"
page_data = {'error': error_msg} page_data['error'] = error_msg
# our default 404 template # if we have no page, let's go to login
else: else:
page_data = {'error': error_msg} self.redirect("public/login")
self.render(template, data=page_data) self.render(template, data=page_data)

View File

@ -1,5 +1,5 @@
{ {
"major": 0.1, "major": 4,
"minor": 1, "minor": 0,
"sub": "Alpha" "sub": "Alpha"
} }

View File

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

View File

@ -6,28 +6,13 @@
<li class="nav-item nav-category" style="margin-top:10px;">Main Menu</li> <li class="nav-item nav-category" style="margin-top:10px;">Main Menu</li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" data-toggle="collapse" href="#dashboard-dropdown" aria-expanded="false" aria-controls="dashboard-dropdown"> <a class="nav-link" href="/panel/dashboard">
<i class="menu-icon fas fa-chart-network"></i> <i class="fas fa-chart-network"></i>&nbsp;
<span class="menu-title">Nodes</span> <span class="menu-title">Dashboard</span>
<i class="menu-arrow"></i>
</a> </a>
<div class="collapse" id="dashboard-dropdown">
<ul class="nav flex-column sub-menu">
<li class="nav-item">
<a class="nav-link" href="/pro/node_names">Nodes Claims</a>
</li>
<li class="nav-item">
<a class="nav-link" href="index.html">Buntu-Server</a>
</li>
<li class="nav-item">
<a class="nav-link" href="pages/dashboards/dashboard-2.html">RMDC-Stone</a>
</li>
</ul>
</div>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" data-toggle="collapse" href="#page-layouts" aria-expanded="false" aria-controls="page-layouts"> <a class="nav-link" data-toggle="collapse" href="#page-layouts" aria-expanded="false" aria-controls="page-layouts">
<i class="menu-icon typcn typcn-archive"></i> <i class="menu-icon typcn typcn-archive"></i>

View File

@ -25,14 +25,14 @@
<div class="col-lg-3 col-md-6"> <div class="col-lg-3 col-md-6">
<div class="d-flex"> <div class="d-flex">
<div class="wrapper"> <div class="wrapper">
<h5 class="mb-1 font-weight-medium text-primary"> Nodes</h5> <h5 class="mb-1 font-weight-medium text-primary"> Host</h5>
<h3 class="mb-0 font-weight-semibold">2 </h3> <h3 class="mb-0 font-weight-semibold"> <i class="fas fa-chart-line"></i></h3>
</div> </div>
<div class="wrapper my-auto ml-auto ml-lg-4"> <div class="wrapper my-auto ml-auto ml-lg-4">
<p class="mb-0 text-success">1 Online</p> <p class="mb-0 text-success">3.5% CPU</p>
<p class="mb-0 text-danger">1 Offline</p> <p class="mb-0 text-danger">80% Memory</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -55,15 +55,7 @@
</div> </div>
<div class="text-block text-center my-3"> <div class="text-block text-center my-3">
<span class="text-small font-weight-semibold">Not a member ?</span> <span class="text-small font-weight-semibold"><a href="https://craftycontrol.com/">Crafty Control {{data['version'] }}</a> </span>
<a href="/public/register" class="text-small">Closed During Alpha Testing</a>
</div>
<div class="text-block text-center my-3">
<span class="text-small font-weight-semibold">Want to know more?</span>
<a href="/public/timeline" class="text-small">See the release timeline</a>
</div>
<div class="text-block text-center my-3">
<span class="text-small font-weight-semibold">Copyright © 2020 Rent My Data Center. All rights reserved.</span>
</div> </div>
</form> </form>

View File

@ -0,0 +1,66 @@
{% extends ../blank_base.html %}
{% block title %}Crafty Controller - Setup 1{% end %}
{% block content %}
<div class="auto-form-wrapper">
<div class="text-center">
<!-- <img src="/static/assets/images/logo_long.jpg">-->
{{ _('Configure Your Existing Server') }}<br /><br />
</div>
<form action="/public/login" method="post">
{% raw xsrf_form_html() %}
<div class="form-group">
<label class="label">
{{ _('Server Name') }} - <small>{{ _('Example Survival Server') }}</small>
</label>
<div class="input-group">
<input type="text" class="form-control" placeholder="{{ _('Server Name') }}" name="server_name" value="{{_('MyFirstServer') }}" maxlength="55">
</div>
</div>
<div class="form-group">
{% if data['is_windows'] %}
<label class="label">
{{ _('Server Path') }} - <small>{{ _('Example c:\minecraft\server') }}</small>
</label>
<div class="input-group">
<input type="text" class="form-control" placeholder="{{ _('Server Path') }}" name="server_path"
value="c:\windows\minecraft" maxlength="255">
</div>
{% else %}
<label class="label">
{{ _('Server Path') }} - <small>{{ _("Example: /var/opt/minecraft/server") }}</small>
</label>
<div class="input-group">
<input type="text" class="form-control" placeholder="{{ _('Server Path') }}" name="server_path"
value="c:\windows\minecraft" maxlength="255">
</div>
{% end %}
</div>
<div class="form-group">
<label class="label">
{{ _('Server Jar') }} - <small>{{ _('Example paper.jar') }}</small>
</label>
<input type="text" class="form-control" placeholder="{{ _('Server Jar') }}" name="server_jar" value="paper.jar" maxlength="255">
</div>
<div class="form-group">
<button class="btn btn-primary submit-btn btn-block">Save</button>
</div>
</form>
</div>
{% end %}

24
main.py
View File

@ -10,21 +10,16 @@ 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.tasks import tasks_manager from app.classes.shared.tasks import tasks_manager
from app.classes.minecraft.controller import controller
def do_intro(): def do_intro():
logger.info("***** Commander Agent Launched *****") logger.info("***** Crafty Controller Started *****")
version_data = helper.get_version() version = helper.get_version_string()
# set some defaults if we don't get version_data from our helper
version = "{}.{}.{}".format(version_data.get('major', '?'),
version_data.get('minor', '?'),
version_data.get('sub', '?'))
intro = """ intro = """
{lines} {lines}
#\t\tWelcome to Crafty Controller - v.{version}\t\t # #\t\tWelcome to Crafty Controller - v.{version}\t\t #
#\t\t\t Codename: Commander \t\t\t\t #
{lines} {lines}
# \tServer Manager / Web Portal for your Minecraft server \t # # \tServer Manager / Web Portal for your Minecraft server \t #
# \t\tHomepage: www.craftycontrol.com\t\t\t # # \t\tHomepage: www.craftycontrol.com\t\t\t #
@ -85,6 +80,10 @@ if __name__ == '__main__':
helper.create_session_file(ignore=args.ignore) helper.create_session_file(ignore=args.ignore)
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
time.sleep(.5)
# this should always be last # this should always be last
tasks_manager.start_main_kill_switch_watcher() tasks_manager.start_main_kill_switch_watcher()
@ -94,7 +93,14 @@ if __name__ == '__main__':
installer.create_tables() installer.create_tables()
installer.default_settings() installer.default_settings()
# our main loop # init servers
logger.info("Initializing all servers defined")
console.info("Initializing all servers defined")
controller.init_all_servers()
servers = controller.list_defined_servers()
# our main loop - eventually a shell
while True: while True:
if tasks_manager.get_main_thread_run_status(): if tasks_manager.get_main_thread_run_status():
sys.exit(0) sys.exit(0)