Merge branch 'download-status' into 'dev'

Add server download status indicator

See merge request crafty-controller/crafty-commander!181
This commit is contained in:
Andrew 2022-03-04 00:36:36 +00:00
commit 3373d6035c
10 changed files with 140 additions and 24 deletions

View File

@ -50,6 +50,18 @@ class Servers_Controller:
def update_server(server_obj): def update_server(server_obj):
return servers_helper.update_server(server_obj) return servers_helper.update_server(server_obj)
@staticmethod
def set_download(server_id):
return servers_helper.set_download(server_id)
@staticmethod
def finish_download(server_id):
return servers_helper.finish_download(server_id)
@staticmethod
def get_download_status(server_id):
return servers_helper.get_download_status(server_id)
@staticmethod @staticmethod
def remove_server(server_id): def remove_server(server_id):
roles_list = server_permissions.get_roles_from_server(server_id) roles_list = server_permissions.get_roles_from_server(server_id)

View File

@ -8,6 +8,9 @@ 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.controllers.servers_controller import Servers_Controller
from app.classes.web.websocket_helper import websocket_helper
from app.classes.models.server_permissions import server_permissions
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -170,21 +173,51 @@ class ServerJars:
response = self._get_api_result(url) response = self._get_api_result(url)
return response return response
def download_jar(self, server, version, path): def download_jar(self, server, version, path, server_id):
update_thread = threading.Thread(target=self.a_download_jar, daemon=True, args=(server, version, path)) update_thread = threading.Thread(target=self.a_download_jar, daemon=True, args=(server, version, path, server_id))
update_thread.start() update_thread.start()
def a_download_jar(self, server, version, path): def a_download_jar(self, server, version, path, server_id):
#delaying download for server register to finish
time.sleep(3)
fetch_url = f"{self.base_url}/api/fetchJar/{server}/{version}" fetch_url = f"{self.base_url}/api/fetchJar/{server}/{version}"
server_users = server_permissions.get_server_user_list(server_id)
#We need to make sure the server is registered before we submit a db update for it's stats.
while True:
try:
Servers_Controller.set_download(server_id)
for user in server_users:
websocket_helper.broadcast_user(user, 'send_start_reload', {
})
break
except:
logger.debug("server not registered yet. Delaying download.")
# open a file stream # open a file stream
with requests.get(fetch_url, timeout=2, stream=True) as r: with requests.get(fetch_url, timeout=2, stream=True) as r:
try: try:
with open(path, 'wb') as output: with open(path, 'wb') as output:
shutil.copyfileobj(r.raw, output) shutil.copyfileobj(r.raw, output)
Servers_Controller.finish_download(server_id)
for user in server_users:
websocket_helper.broadcast_user(user, 'notification', "Executable download finished")
time.sleep(3)
websocket_helper.broadcast_user(user, 'send_start_reload', {
})
return True
except Exception as e: except Exception as e:
logger.error(f"Unable to save jar to {path} due to error:{e}") logger.error(f"Unable to save jar to {path} due to error:{e}")
Servers_Controller.finish_download(server_id)
server_users = server_permissions.get_server_user_list(server_id)
for user in server_users:
websocket_helper.broadcast_user(user, 'notification', "Executable download finished")
time.sleep(3)
websocket_helper.broadcast_user(user, 'send_start_reload', {
})
return False return False

View File

@ -75,6 +75,7 @@ class Server_Stats(Model):
waiting_start = BooleanField(default=False) waiting_start = BooleanField(default=False)
first_run = BooleanField(default=True) first_run = BooleanField(default=True)
crashed = BooleanField(default=False) crashed = BooleanField(default=False)
downloading = BooleanField(default=False)
class Meta: class Meta:
@ -194,6 +195,22 @@ class helper_servers:
with database.atomic(): with database.atomic():
Server_Stats.update(crashed=True).where(Server_Stats.server_id == server_id).execute() Server_Stats.update(crashed=True).where(Server_Stats.server_id == server_id).execute()
@staticmethod
def set_download(server_id):
with database.atomic():
Server_Stats.update(downloading=True).where(Server_Stats.server_id == server_id).execute()
@staticmethod
def finish_download(server_id):
with database.atomic():
Server_Stats.update(downloading=False).where(Server_Stats.server_id == server_id).execute()
@staticmethod
def get_download_status(server_id):
download_status = Server_Stats.select().where(Server_Stats.server_id == server_id).get()
return download_status.downloading
@staticmethod @staticmethod
def server_crash_reset(server_id): def server_crash_reset(server_id):
with database.atomic(): with database.atomic():

View File

@ -292,11 +292,12 @@ class Controller:
server_log_file = f"{server_dir}/logs/latest.log" server_log_file = f"{server_dir}/logs/latest.log"
server_stop = "stop" server_stop = "stop"
# download the jar
server_jar_obj.download_jar(server, version, full_jar_path)
new_id = self.register_server(name, server_id, server_dir, backup_path, server_command, server_file, server_log_file, server_stop, new_id = self.register_server(name, server_id, server_dir, backup_path, server_command, server_file, server_log_file, server_stop,
port, server_type='minecraft-java') port, server_type='minecraft-java')
# download the jar
server_jar_obj.download_jar(server, version, full_jar_path, new_id)
return new_id return new_id
@staticmethod @staticmethod

View File

@ -188,6 +188,13 @@ class Server:
else: else:
user_lang = users_helper.get_user_lang_by_id(user_id) user_lang = users_helper.get_user_lang_by_id(user_id)
if servers_helper.get_download_status(self.server_id):
if user_id:
websocket_helper.broadcast_user(user_id, 'send_start_error',{
'error': translation.translate('error', 'not-downloaded', user_lang)
})
return False
logger.info(f"Start command detected. Reloading settings from DB for server {self.name}") logger.info(f"Start command detected. Reloading settings from DB for server {self.name}")
self.setup_server_run_command() self.setup_server_run_command()
# fail safe in case we try to start something already running # fail safe in case we try to start something already running

View File

@ -150,7 +150,7 @@ class PanelHandler(BaseHandler):
else: else:
if not self.controller.servers.server_id_authorized(server_id, exec_user["user_id"]): if not self.controller.servers.server_id_authorized(server_id, exec_user["user_id"]):
print(f'User {exec_user["user_id"]} does not have permission') print(f'User {exec_user["user_id"]} does not have permission')
self.redirect("/panel/error?error=Invalid Server ID") self.redirect("/pandel/error?error=Invalid Server ID")
return None return None
return server_id return server_id
@ -209,14 +209,17 @@ class PanelHandler(BaseHandler):
user_order = user_order['server_order'].split(',') user_order = user_order['server_order'].split(',')
page_servers = [] page_servers = []
server_ids = [] server_ids = []
un_used_servers = defined_servers
for server_id in user_order: for server_id in user_order:
for server in defined_servers: for server in un_used_servers:
if str(server['server_id']) == str(server_id): if str(server['server_id']) == str(server_id):
page_servers.append(server) page_servers.append(server)
un_used_servers.remove(server)
user_order.remove(server_id)
for server in defined_servers: for server in un_used_servers:
server_ids.append(str(server['server_id'])) server_ids.append(str(server['server_id']))
if server not in page_servers: if server not in page_servers:
page_servers.append(server) page_servers.append(server)
@ -335,14 +338,33 @@ class PanelHandler(BaseHandler):
user_order = user_order['server_order'].split(',') user_order = user_order['server_order'].split(',')
page_servers = [] page_servers = []
server_ids = [] server_ids = []
un_used_servers = page_data['servers']
flag = 0
for server_id in user_order: for server_id in user_order:
for server in page_data['servers']: for server in un_used_servers:
if flag == 0:
server['stats']['downloading'] = self.controller.servers.get_download_status(
str(server['stats']['server_id']['server_id']))
server['stats']['crashed'] = self.controller.servers.is_crashed(
str(server['stats']['server_id']['server_id']))
try:
server['stats']['waiting_start'] = self.controller.servers.get_waiting_start(
str(server['stats']['server_id']['server_id']))
except Exception as e:
logger.error(f"Failed to get server waiting to start: {e}")
server['stats']['waiting_start'] = False
if str(server['server_data']['server_id']) == str(server_id): if str(server['server_data']['server_id']) == str(server_id):
page_servers.append(server) page_servers.append(server)
un_used_servers.remove(server)
user_order.remove(server_id)
#we only want to set these server stats values once. We need to update the flag so it only hits that if once.
flag += 1
for server in page_data['servers']:
for server in un_used_servers:
server_ids.append(str(server['server_data']['server_id'])) server_ids.append(str(server['server_data']['server_id']))
if server not in page_servers: if server not in page_servers:
page_servers.append(server) page_servers.append(server)
@ -352,17 +374,6 @@ class PanelHandler(BaseHandler):
user_order.remove(server_id) user_order.remove(server_id)
page_data['servers'] = page_servers page_data['servers'] = page_servers
for data in page_data['servers']:
data['stats']['crashed'] = self.controller.servers.is_crashed(
str(data['stats']['server_id']['server_id']))
try:
data['stats']['waiting_start'] = self.controller.servers.get_waiting_start(
str(data['stats']['server_id']['server_id']))
except Exception as e:
logger.error(f"Failed to get server waiting to start: {e}")
data['stats']['waiting_start'] = False
try: try:
self.fetch_server_data(page_data) self.fetch_server_data(page_data)
except: except:
@ -390,6 +401,8 @@ class PanelHandler(BaseHandler):
# 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'] = self.controller.servers.get_server_data_by_id(server_id) page_data['server_data'] = self.controller.servers.get_server_data_by_id(server_id)
page_data['server_stats'] = self.controller.servers.get_server_stats_by_id(server_id) page_data['server_stats'] = self.controller.servers.get_server_stats_by_id(server_id)
page_data['downloading'] = self.controller.servers.get_download_status(
server_id)
try: try:
page_data['waiting_start'] = self.controller.servers.get_waiting_start(server_id) page_data['waiting_start'] = self.controller.servers.get_waiting_start(server_id)
except Exception as e: except Exception as e:

View File

@ -158,6 +158,9 @@
<a data-id="{{server['server_data']['server_id']}}" class="" title={{ <a data-id="{{server['server_data']['server_id']}}" class="" title={{
translate('dashboard', 'delay-explained' , data['lang'])}}>{{ translate('dashboard', 'starting', translate('dashboard', 'delay-explained' , data['lang'])}}>{{ translate('dashboard', 'starting',
data['lang']) }}</i></a> data['lang']) }}</i></a>
{% elif server['stats']['downloading']%}
<a data-id="{{server['server_data']['server_id']}}" class=""><i class="fa fa-spinner fa-spin"></i> {{ translate('serverTerm', 'downloading',
data['lang']) }}</a>
{% else %} {% else %}
<a data-id="{{server['server_data']['server_id']}}" class="play_button" <a data-id="{{server['server_data']['server_id']}}" class="play_button"
data-toggle="tooltip" title="{{ translate('dashboard', 'start' , data['lang']) }}"> data-toggle="tooltip" title="{{ translate('dashboard', 'start' , data['lang']) }}">

View File

@ -59,6 +59,13 @@
<button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart', data['lang']) %}</button> <button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart', data['lang']) %}</button>
<button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button> <button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
</div> </div>
{% elif data['downloading'] %}
<div id="control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible">
<button onclick="" id="start-btn" style="max-width: 12rem; white-space: nowrap;" class="btn btn-secondary m-1 flex-grow-1 disabled"><i class="fa fa-spinner fa-spin"></i> {{ translate('serverTerm', 'downloading',
data['lang']) }}</button>
<button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart', data['lang']) %}</button>
<button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
</div>
{% else %} {% else %}
<div id="control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible"> <div id="control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible">
<button onclick="send_command(serverId, 'start_server');" id="start-btn" style="max-width: 7rem;" class="btn btn-primary m-1 flex-grow-1">{{ translate('serverTerm', 'start', data['lang']) }}</button> <button onclick="send_command(serverId, 'start_server');" id="start-btn" style="max-width: 7rem;" class="btn btn-primary m-1 flex-grow-1">{{ translate('serverTerm', 'start', data['lang']) }}</button>
@ -153,6 +160,12 @@
restartBtn.setAttribute('disabled', 'disabled'); restartBtn.setAttribute('disabled', 'disabled');
stopBtn.setAttribute('disabled', 'disabled'); stopBtn.setAttribute('disabled', 'disabled');
} }
if (webSocket) {
webSocket.on('send_start_reload', function () {
location.reload()
});
}
//{% end %} //{% end %}
function get_server_log() { function get_server_log() {

View File

@ -0,0 +1,16 @@
# Generated by database migrator
import peewee
def migrate(migrator, database, **kwargs):
migrator.add_columns('server_stats', downloading=peewee.BooleanField(default=False))
"""
Write your migrations here.
"""
def rollback(migrator, database, **kwargs):
migrator.drop_columns('server_stats', ['downloading'])
"""
Write your rollback migrations here.
"""

View File

@ -165,7 +165,8 @@
"stop": "Stop", "stop": "Stop",
"updating": "Updating...", "updating": "Updating...",
"starting": "Delayed-Start", "starting": "Delayed-Start",
"delay-explained": "The service/agent has recently started and is delaying the start of the minecraft server instance" "delay-explained": "The service/agent has recently started and is delaying the start of the minecraft server instance",
"downloading": "Downloading..."
}, },
"serverPlayerManagement": { "serverPlayerManagement": {
"players": "Players", "players": "Players",