Error handling if server was deleted without crafty knowing

start/stop server functions complete
expanding out online/max/motd etc in db for easier parsing
fixed issue of servers loosing their name from 3.2
finished restart button
added fontawesome 5
This commit is contained in:
Phillip Tarrant 2020-08-24 19:11:17 -04:00
parent a958b6dea0
commit d0720958f9
54 changed files with 80623 additions and 50 deletions

View File

@ -86,20 +86,18 @@ class Controller:
def list_running_servers(self):
running_servers = []
if len(self.servers_list) > 0:
# for each server
for s in self.servers_list:
# 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
})
# 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

View File

@ -1,4 +1,5 @@
import os
import sys
import re
import json
import time
@ -9,14 +10,23 @@ 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__)
try:
from pexpect.popen_spawn import PopenSpawn
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 Server:
def __init__(self):
@ -93,6 +103,8 @@ class Server:
helper.do_exit()
def start_server(self):
from app.classes.minecraft.stats import stats
# fail safe in case we try to start something already running
if self.check_running():
logger.error("Server is already running - Cancelling Startup")
@ -143,6 +155,7 @@ class Server:
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
stats.record_stats()
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))
@ -161,16 +174,59 @@ class Server:
self.server_thread.join()
def stop_server(self):
if self.settings['stop_command_needed']:
from app.classes.minecraft.stats import stats
if self.settings['stop_command']:
self.send_command(self.settings['stop_command'])
running = self.check_running()
x = 0
# caching the name and pid number
server_name = self.name
server_pid = self.PID
while running:
x = x+1
logger.info("Server {} is still running - waiting 2s to see if it stops".format(server_name))
console.info("Server {} is still running - waiting 2s to see if it stops".format(server_name))
console.info("Server has {} seconds to respond before we force it down".format(int(60-(x*2))))
running = self.check_running()
time.sleep(2)
# if we haven't closed in 60 seconds, let's just slam down on the PID
if x >= 30:
logger.info("Server {} is still running - Forcing the process down".format(server_name))
console.info("Server {} is still running - Forcing the process down".format(server_name))
self.killpid(server_pid)
logger.info("Stopped Server {} with PID {}".format(server_name, server_pid))
console.info("Stopped Server {} with PID {}".format(server_name, server_pid))
else:
self.killpid(self.PID)
# massive resetting of variables
self.cleanup_server_object()
stats.record_stats()
def restart_threaded_server(self):
# if not already running, let's just start
if not self.check_running():
self.run_threaded_server()
else:
self.stop_threaded_server()
time.sleep(2)
self.run_threaded_server()
def cleanup_server_object(self):
self.process = None
self.PID = None
self.start_time = None
self.name = None
self.restart_count = 0
self.is_crashed = False
self.updating = False
self.process = None
def check_running(self, shutting_down=False):
# if process is None, we never tried to start
@ -217,9 +273,7 @@ class Server:
self.is_crashed = True
return False
self.process = None
self.PID = None
self.name = None
self.cleanup_server_object()
return False
return True

View File

@ -156,7 +156,7 @@ class ServerJars:
logger.error("Got {} code from download, escaping".format(r.status_code))
return False
# todo: build server
# todo: import server
def build_server(self, server: str, version: str, name: str, min_mem: int, max_mem: int, port: int):
server_id = helper.create_uuid()
server_dir = os.path.join(helper.servers_dir, server_id)
@ -177,7 +177,7 @@ class ServerJars:
Servers.server_uuid: server_id,
Servers.path: server_dir,
Servers.executable: jar_file,
Servers.execution_command: 'java -Xms{}G -Xmx{}G -jar /var/opt/minecraft/server/paperclip.jar nogui'.format(min_mem, max_mem),
Servers.execution_command: 'java -Xms{}G -Xmx{}G -jar {} nogui'.format(min_mem, max_mem, full_jar_path),
Servers.auto_start: False,
Servers.auto_start_delay: 10,
Servers.crash_detection: False,

View File

@ -2,7 +2,6 @@ import os
import json
import time
import psutil
#import requests
import logging
import datetime
@ -181,10 +180,13 @@ class Stats:
logger.debug("Pinging {} on port {}".format(internal_ip, server_port))
int_mc_ping = ping(internal_ip, int(server_port))
int_data = "Unable to connect"
int_data = False
ping_data = {}
# if we got a good ping return, let's parse it
if int_mc_ping:
int_data = self.parse_server_ping(int_mc_ping)
int_data = True
ping_data = self.parse_server_ping(int_mc_ping)
server_stats = {
'id': server_id,
@ -195,7 +197,12 @@ class Stats:
'world_name': world_name,
'world_size': self.get_world_size(world_path),
'server_port': server_port,
'int_ping_results': int_data
'int_ping_results': int_data,
'online': ping_data.get("online", False),
"max": ping_data.get("max", False),
'players': ping_data.get("players", False),
'desc': ping_data.get("server_description", False),
'version': ping_data.get("server_version", False)
}
# add this servers data to the stack
@ -223,6 +230,7 @@ class Stats:
server_stats = stats_to_send.get('servers')
for server in server_stats:
Server_Stats.insert({
Server_Stats.server_id: server.get('id', 0),
Server_Stats.started: server.get('started', ""),
@ -232,7 +240,12 @@ class Stats:
Server_Stats.world_name: server.get('world_name', ""),
Server_Stats.world_size: server.get('world_size', ""),
Server_Stats.server_port: server.get('server_port', ""),
Server_Stats.int_ping_results: server.get('int_ping_results', ""),
Server_Stats.int_ping_results: server.get('int_ping_results', False),
Server_Stats.online: server.get("online", False),
Server_Stats.max: server.get("max", False),
Server_Stats.players: server.get("players", False),
Server_Stats.desc: server.get("desc", False),
Server_Stats.version: server.get("version", False)
}).execute()
# delete 1 week old data

View File

@ -28,6 +28,8 @@ class BaseModel(Model):
class Meta:
database = database
# todo: access logs
class Users(BaseModel):
user_id = AutoField()
@ -91,11 +93,29 @@ class Server_Stats(BaseModel):
world_size = CharField(default="")
server_port = IntegerField(default=25565)
int_ping_results = CharField(default="")
online = IntegerField(default=0)
max = IntegerField(default=0)
players = CharField(default="")
desc = CharField(default="Unable to Connect")
version = CharField(default="")
class Meta:
table_name = "server_stats"
class Commands(BaseModel):
command_id = AutoField()
created = DateTimeField(default=datetime.datetime.now)
server_id = ForeignKeyField(Servers, backref='server')
user = ForeignKeyField(Users, backref='user')
source_ip = CharField(default='127.0.0.1')
command = CharField(default='')
class Meta:
table_name = "commands"
class Webhooks(BaseModel):
id = AutoField()
name = CharField(max_length=64, unique=True)
@ -129,7 +149,8 @@ class db_builder:
Host_Stats,
Webhooks,
Servers,
Server_Stats
Server_Stats,
Commands
])
@staticmethod

View File

@ -48,7 +48,13 @@ class PanelHandler(BaseHandler):
elif page == 'dashboard':
page_data['servers'] = db_helper.get_all_servers_stats()
for s in page_data['servers']:
try:
data = json.loads(s['int_ping_results'])
print(data)
s['int_ping_results'] = data
except:
pass
template = "panel/dashboard.html"
self.render(

View File

@ -31,6 +31,8 @@ class ServerHandler(BaseHandler):
template = "public/404.html"
defined_servers = controller.list_defined_servers()
page_data = {
'version_data': "version_data_here",
'user_data': user_data,
@ -39,7 +41,8 @@ class ServerHandler(BaseHandler):
'running': len(controller.list_running_servers()),
'stopped': (len(controller.list_defined_servers()) - len(controller.list_running_servers()))
},
'hosts_data': db_helper.get_latest_hosts_stats()
'hosts_data': db_helper.get_latest_hosts_stats(),
'menu_servers': defined_servers,
}
@ -64,6 +67,22 @@ class ServerHandler(BaseHandler):
'user_data': user_data,
}
if page == "command":
server_id = bleach.clean(self.get_argument("id", None))
command = bleach.clean(self.get_argument("command", None))
if server_id is not None:
svr = controller.get_server_obj(server_id)
if command == "start_server":
svr.run_threaded_server()
if command == "stop_server":
svr.stop_threaded_server()
if command == "restart_server":
svr.restart_threaded_server()
if page == "step1":
server = bleach.clean(self.get_argument('server', ''))
@ -74,11 +93,10 @@ class ServerHandler(BaseHandler):
server_parts = server.split("|")
success = server_jar_obj.build_server(server_parts[0], server_parts[1],server_name,min_mem, max_mem, port)
success = server_jar_obj.build_server(server_parts[0], server_parts[1], server_name, min_mem, max_mem, port)
if success:
self.redirect("/panel/dashboard")
self.render(
template,
data=page_data

View File

@ -1,5 +1,5 @@
{
"last_refreshed": "08/23/2020, 17:45:25",
"last_refreshed": "08/24/2020, 17:47:14",
"servers": {
"nukkitx": [
"1.14"

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,15 @@
/*!
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license (Commercial License)
*/
@font-face {
font-family: 'Font Awesome 5 Brands';
font-style: normal;
font-weight: 400;
font-display: block;
src: url("../webfonts/fa-brands-400.eot");
src: url("../webfonts/fa-brands-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.woff") format("woff"), url("../webfonts/fa-brands-400.ttf") format("truetype"), url("../webfonts/fa-brands-400.svg#fontawesome") format("svg"); }
.fab {
font-family: 'Font Awesome 5 Brands';
font-weight: 400; }

View File

@ -0,0 +1,5 @@
/*!
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license (Commercial License)
*/
@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands";font-weight:400}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,15 @@
/*!
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license (Commercial License)
*/
@font-face {
font-family: 'Font Awesome 5 Pro';
font-style: normal;
font-weight: 300;
font-display: block;
src: url("../webfonts/fa-light-300.eot");
src: url("../webfonts/fa-light-300.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-light-300.woff2") format("woff2"), url("../webfonts/fa-light-300.woff") format("woff"), url("../webfonts/fa-light-300.ttf") format("truetype"), url("../webfonts/fa-light-300.svg#fontawesome") format("svg"); }
.fal {
font-family: 'Font Awesome 5 Pro';
font-weight: 300; }

View File

@ -0,0 +1,5 @@
/*!
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license (Commercial License)
*/
@font-face{font-family:"Font Awesome 5 Pro";font-style:normal;font-weight:300;font-display:block;src:url(../webfonts/fa-light-300.eot);src:url(../webfonts/fa-light-300.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-light-300.woff2) format("woff2"),url(../webfonts/fa-light-300.woff) format("woff"),url(../webfonts/fa-light-300.ttf) format("truetype"),url(../webfonts/fa-light-300.svg#fontawesome) format("svg")}.fal{font-family:"Font Awesome 5 Pro";font-weight:300}

View File

@ -0,0 +1,15 @@
/*!
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license (Commercial License)
*/
@font-face {
font-family: 'Font Awesome 5 Pro';
font-style: normal;
font-weight: 400;
font-display: block;
src: url("../webfonts/fa-regular-400.eot");
src: url("../webfonts/fa-regular-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.woff") format("woff"), url("../webfonts/fa-regular-400.ttf") format("truetype"), url("../webfonts/fa-regular-400.svg#fontawesome") format("svg"); }
.far {
font-family: 'Font Awesome 5 Pro';
font-weight: 400; }

View File

@ -0,0 +1,5 @@
/*!
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license (Commercial License)
*/
@font-face{font-family:"Font Awesome 5 Pro";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.eot);src:url(../webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.woff) format("woff"),url(../webfonts/fa-regular-400.ttf) format("truetype"),url(../webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:"Font Awesome 5 Pro";font-weight:400}

View File

@ -0,0 +1,16 @@
/*!
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license (Commercial License)
*/
@font-face {
font-family: 'Font Awesome 5 Pro';
font-style: normal;
font-weight: 900;
font-display: block;
src: url("../webfonts/fa-solid-900.eot");
src: url("../webfonts/fa-solid-900.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.woff") format("woff"), url("../webfonts/fa-solid-900.ttf") format("truetype"), url("../webfonts/fa-solid-900.svg#fontawesome") format("svg"); }
.fa,
.fas {
font-family: 'Font Awesome 5 Pro';
font-weight: 900; }

View File

@ -0,0 +1,5 @@
/*!
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license (Commercial License)
*/
@font-face{font-family:"Font Awesome 5 Pro";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:"Font Awesome 5 Pro";font-weight:900}

View File

@ -0,0 +1,371 @@
/*!
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license (Commercial License)
*/
svg:not(:root).svg-inline--fa {
overflow: visible; }
.svg-inline--fa {
display: inline-block;
font-size: inherit;
height: 1em;
overflow: visible;
vertical-align: -.125em; }
.svg-inline--fa.fa-lg {
vertical-align: -.225em; }
.svg-inline--fa.fa-w-1 {
width: 0.0625em; }
.svg-inline--fa.fa-w-2 {
width: 0.125em; }
.svg-inline--fa.fa-w-3 {
width: 0.1875em; }
.svg-inline--fa.fa-w-4 {
width: 0.25em; }
.svg-inline--fa.fa-w-5 {
width: 0.3125em; }
.svg-inline--fa.fa-w-6 {
width: 0.375em; }
.svg-inline--fa.fa-w-7 {
width: 0.4375em; }
.svg-inline--fa.fa-w-8 {
width: 0.5em; }
.svg-inline--fa.fa-w-9 {
width: 0.5625em; }
.svg-inline--fa.fa-w-10 {
width: 0.625em; }
.svg-inline--fa.fa-w-11 {
width: 0.6875em; }
.svg-inline--fa.fa-w-12 {
width: 0.75em; }
.svg-inline--fa.fa-w-13 {
width: 0.8125em; }
.svg-inline--fa.fa-w-14 {
width: 0.875em; }
.svg-inline--fa.fa-w-15 {
width: 0.9375em; }
.svg-inline--fa.fa-w-16 {
width: 1em; }
.svg-inline--fa.fa-w-17 {
width: 1.0625em; }
.svg-inline--fa.fa-w-18 {
width: 1.125em; }
.svg-inline--fa.fa-w-19 {
width: 1.1875em; }
.svg-inline--fa.fa-w-20 {
width: 1.25em; }
.svg-inline--fa.fa-pull-left {
margin-right: .3em;
width: auto; }
.svg-inline--fa.fa-pull-right {
margin-left: .3em;
width: auto; }
.svg-inline--fa.fa-border {
height: 1.5em; }
.svg-inline--fa.fa-li {
width: 2em; }
.svg-inline--fa.fa-fw {
width: 1.25em; }
.fa-layers svg.svg-inline--fa {
bottom: 0;
left: 0;
margin: auto;
position: absolute;
right: 0;
top: 0; }
.fa-layers {
display: inline-block;
height: 1em;
position: relative;
text-align: center;
vertical-align: -.125em;
width: 1em; }
.fa-layers svg.svg-inline--fa {
-webkit-transform-origin: center center;
transform-origin: center center; }
.fa-layers-text, .fa-layers-counter {
display: inline-block;
position: absolute;
text-align: center; }
.fa-layers-text {
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
-webkit-transform-origin: center center;
transform-origin: center center; }
.fa-layers-counter {
background-color: #ff253a;
border-radius: 1em;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: #fff;
height: 1.5em;
line-height: 1;
max-width: 5em;
min-width: 1.5em;
overflow: hidden;
padding: .25em;
right: 0;
text-overflow: ellipsis;
top: 0;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: top right;
transform-origin: top right; }
.fa-layers-bottom-right {
bottom: 0;
right: 0;
top: auto;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: bottom right;
transform-origin: bottom right; }
.fa-layers-bottom-left {
bottom: 0;
left: 0;
right: auto;
top: auto;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: bottom left;
transform-origin: bottom left; }
.fa-layers-top-right {
right: 0;
top: 0;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: top right;
transform-origin: top right; }
.fa-layers-top-left {
left: 0;
right: auto;
top: 0;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: top left;
transform-origin: top left; }
.fa-lg {
font-size: 1.33333em;
line-height: 0.75em;
vertical-align: -.0667em; }
.fa-xs {
font-size: .75em; }
.fa-sm {
font-size: .875em; }
.fa-1x {
font-size: 1em; }
.fa-2x {
font-size: 2em; }
.fa-3x {
font-size: 3em; }
.fa-4x {
font-size: 4em; }
.fa-5x {
font-size: 5em; }
.fa-6x {
font-size: 6em; }
.fa-7x {
font-size: 7em; }
.fa-8x {
font-size: 8em; }
.fa-9x {
font-size: 9em; }
.fa-10x {
font-size: 10em; }
.fa-fw {
text-align: center;
width: 1.25em; }
.fa-ul {
list-style-type: none;
margin-left: 2.5em;
padding-left: 0; }
.fa-ul > li {
position: relative; }
.fa-li {
left: -2em;
position: absolute;
text-align: center;
width: 2em;
line-height: inherit; }
.fa-border {
border: solid 0.08em #eee;
border-radius: .1em;
padding: .2em .25em .15em; }
.fa-pull-left {
float: left; }
.fa-pull-right {
float: right; }
.fa.fa-pull-left,
.fas.fa-pull-left,
.far.fa-pull-left,
.fal.fa-pull-left,
.fab.fa-pull-left {
margin-right: .3em; }
.fa.fa-pull-right,
.fas.fa-pull-right,
.far.fa-pull-right,
.fal.fa-pull-right,
.fab.fa-pull-right {
margin-left: .3em; }
.fa-spin {
-webkit-animation: fa-spin 2s infinite linear;
animation: fa-spin 2s infinite linear; }
.fa-pulse {
-webkit-animation: fa-spin 1s infinite steps(8);
animation: fa-spin 1s infinite steps(8); }
@-webkit-keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }
@keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }
.fa-rotate-90 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
-webkit-transform: rotate(90deg);
transform: rotate(90deg); }
.fa-rotate-180 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
-webkit-transform: rotate(180deg);
transform: rotate(180deg); }
.fa-rotate-270 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
-webkit-transform: rotate(270deg);
transform: rotate(270deg); }
.fa-flip-horizontal {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
-webkit-transform: scale(-1, 1);
transform: scale(-1, 1); }
.fa-flip-vertical {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-webkit-transform: scale(1, -1);
transform: scale(1, -1); }
.fa-flip-both, .fa-flip-horizontal.fa-flip-vertical {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-webkit-transform: scale(-1, -1);
transform: scale(-1, -1); }
:root .fa-rotate-90,
:root .fa-rotate-180,
:root .fa-rotate-270,
:root .fa-flip-horizontal,
:root .fa-flip-vertical,
:root .fa-flip-both {
-webkit-filter: none;
filter: none; }
.fa-stack {
display: inline-block;
height: 2em;
position: relative;
width: 2.5em; }
.fa-stack-1x,
.fa-stack-2x {
bottom: 0;
left: 0;
margin: auto;
position: absolute;
right: 0;
top: 0; }
.svg-inline--fa.fa-stack-1x {
height: 1em;
width: 1.25em; }
.svg-inline--fa.fa-stack-2x {
height: 2em;
width: 2.5em; }
.fa-inverse {
color: #fff; }
.sr-only {
border: 0;
clip: rect(0, 0, 0, 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px; }
.sr-only-focusable:active, .sr-only-focusable:focus {
clip: auto;
height: auto;
margin: 0;
overflow: visible;
position: static;
width: auto; }
.svg-inline--fa .fa-primary {
fill: var(--fa-primary-color, currentColor);
opacity: 1;
opacity: var(--fa-primary-opacity, 1); }
.svg-inline--fa .fa-secondary {
fill: var(--fa-secondary-color, currentColor);
opacity: 0.4;
opacity: var(--fa-secondary-opacity, 0.4); }
.svg-inline--fa.fa-swap-opacity .fa-primary {
opacity: 0.4;
opacity: var(--fa-secondary-opacity, 0.4); }
.svg-inline--fa.fa-swap-opacity .fa-secondary {
opacity: 1;
opacity: var(--fa-primary-opacity, 1); }
.svg-inline--fa mask .fa-primary,
.svg-inline--fa mask .fa-secondary {
fill: black; }
.fad.fa-inverse {
color: #fff; }

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 713 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 2.5 MiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 2.3 MiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 2.1 MiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

@ -11,6 +11,7 @@
<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/fontawesome5/css/all.css">
<link rel="stylesheet" href="/static/assets/vendors/css/vendor.bundle.base.css">
<!-- endinject -->
@ -96,8 +97,19 @@
<script src="/static/assets/js/shared/hoverable-collapse.js"></script>
<script src="/static/assets/js/shared/misc.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/bootbox.js/5.4.0/bootbox.min.js"></script>
<script src="//kit.fontawesome.com/b539899a58.js" crossorigin="anonymous"></script>
<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;
}
<!-- tool tips -->
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
</script>
{% block js %}
<!-- Custom js for this page -->

View File

@ -15,7 +15,7 @@
<li class="nav-item">
<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="fas fa-server"></i> &nbsp;
<span class="menu-title">Servers</span>
<i class="menu-arrow"></i>
</a>
@ -32,7 +32,7 @@
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="collapse" href="#apps-dropdown" aria-expanded="false" aria-controls="apps-dropdown">
<i class="menu-icon typcn typcn-arrow-maximise"></i>
<i class="fas fa-cogs"></i> &nbsp;
<span class="menu-title">Configuration</span>
<i class="menu-arrow"></i>
</a>
@ -60,15 +60,29 @@
<li class="nav-item">
<a class="nav-link" href="pages/samples/widgets.html">
<i class="menu-icon typcn typcn-document-text"></i>
<i class="fas fa-book"></i> &nbsp;
<span class="menu-title">Documentation</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="pages/samples/widgets.html">
<i class="menu-icon fab fa-discord"></i>
<span class="menu-title">Commander Discord</span>
<i class="fab fa-discord"></i> &nbsp;
<span class="menu-title">Discord</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="pages/samples/widgets.html">
<i class="fas fa-heart"></i> &nbsp;
<span class="menu-title">Credits</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="pages/samples/widgets.html">
<i class="fas fa-donate"></i> &nbsp;
<span class="menu-title">Contribute</span>
</a>
</li>

View File

@ -113,25 +113,25 @@
</td>
<td>
{% if server['started'] == True %}
<a hidden data-id="server['server_id']['server_id']" class="play_button hidden"><i class="fas fa-play"></i></a> &nbsp;
<a data-id="server['server_id']['server_id']" class="stop_button"><i class="fas fa-stop"></i></a> &nbsp;
<a data-id="server['server_id']['server_id']" class="restart_button"><i class="fas fa-sync"></i></a> &nbsp;
{% if server['running']%}
<a class="stop_button" data-id="{{server['server_id']['server_id']}}"> <i class="fas fa-stop"></i></a> &nbsp;
<a class="restart_button" data-id="{{server['server_id']['server_id']}}"> <i class="fas fa-sync"></i></a> &nbsp;
{% else %}
<a data-id="server['server_id']['server_id']" class="play_button"><i class="fas fa-play"></i></a> &nbsp;
<a hidden data-id="server['server_id']['server_id']" class="stop_button hidden"><i class="fas fa-stop"></i></a> &nbsp;
<a hidden data-id="server['server_id']['server_id']" class="restart_button hidden"><i class="fas fa-sync"></i></a> &nbsp;
<a data-id="{{server['server_id']['server_id']}}" class="play_button"><i class="fas fa-play"></i></a> &nbsp;
{% end %}
</td>
<td>
<div class="progress">
<div class="progress" data-toggle="tooltip" data-placement="top" title="{{server['cpu']}}">
<div class="progress-bar bg-success" role="progressbar" style="width: {{server['cpu']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
{{server['cpu']}}
</td>
<td>
<div class="progress">
<div class="progress" data-toggle="tooltip" data-placement="top" title="{{server['mem']}}">
<div class="progress-bar bg-danger" role="progressbar" style="{{server['mem']}}%" aria-valuenow="24" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</td>
@ -139,10 +139,16 @@
{{ server['world_name'] }} : {{ server['world_size'] }}
</td>
<td>
{{ server['int_ping_results'] }}
{% if server['int_ping_results'] == True %}
{{ server['online'] }} / {{ server['max'] }} Max<br />
{{ server['desc'] }} <br />
{{ server['version'] }}
{% else %}
{{ _("Unable to connect to server") }}
{% end %}
</td>
<td>
{% if server['started'] == True %}
{% if server['running'] %}
<i class="fas fa-thumbs-up"></i> <span class="text-success">Online</span>
{% else %}
<i class="fas fa-thumbs-down"></i> <span class="text-danger">Offline</span>
@ -166,4 +172,63 @@
{% end %}
{% block js %}
<script>
function send_command (server_id, command){
<!-- this getCookie function is in base.html-->
var token = getCookie("_xsrf");
$.ajax({
type: "POST",
headers: {'X-XSRFToken': token},
url: '/server/command?command=' + command + '&id=' + server_id,
success: function(data){
console.log("got response:");
console.log(data);
setTimeout(function(){ location.reload(); }, 10000);
}
});
}
$( document ).ready(function() {
console.log('ready for JS!')
$( ".play_button" ).click(function() {
server_id = $(this).attr("data-id");
send_command(server_id, 'start_server');
bootbox.alert({
backdrop: true,
title: "Sending your command",
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; Please be patient while we start the server<br /> This screen will refresh in a moment </div>'
});
});
$( ".stop_button" ).click(function() {
console.log("stopping server");
server_id = $(this).attr("data-id");
send_command(server_id, 'stop_server');
bootbox.alert({
backdrop: true,
title: "Sending your command",
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; Please be patient while we stop the server<br /> This screen will refresh in a moment </div>'
});
});
$( ".restart_button" ).click(function() {
server_id = $(this).attr("data-id");
send_command(server_id, 'restart_server');
bootbox.alert({
backdrop: true,
title: "Sending your command",
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; Please be patient while we restart the server<br /> This screen will refresh in a moment </div>'
});
});
});
</script>
{% end %}