From 2528c123f2f65649223d24cf3ea20bb4f3eeb026 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 13 Sep 2021 13:10:34 -0400 Subject: [PATCH 1/2] Adds port/internet connection checks. Checks when crafty starts for internet and a port forward for Crafty's https port. Checks on server launch for internet connection and for port forwarding for executed server. --- app/classes/shared/helpers.py | 21 +++++++++++++++++++++ app/classes/shared/server.py | 16 ++++++++++++++-- app/frontend/templates/base.html | 7 ++++++- main.py | 5 +++++ 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 7a37f799..0937f764 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -14,6 +14,7 @@ import html import zipfile import pathlib import shutil +from requests import get from datetime import datetime from socket import gethostname @@ -76,6 +77,26 @@ class Helpers: logger.error("{} does not exist".format(file)) return True + @staticmethod + def check_internet(): + try: + requests.get('https://google.com', timeout=1) + return True + except Exception as err: + return False + + @staticmethod + def check_port(server_port): + host_public = get('https://api.ipify.org').text + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + result = sock.connect_ex((host_public ,server_port)) + sock.close() + if result == 0: + return True + else: + return False + + def check_for_old_logs(self, db_helper): servers = db_helper.get_all_defined_servers() for server in servers: diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index a504c15c..49f463f9 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -191,8 +191,20 @@ class Server: 'error': msg }) return False - websocket_helper.broadcast('send_start_reload', { - }) + if helper.check_internet(): + loc_server_port = db_helper.get_server_stats_by_id(self.server_id)['server_port'] + if helper.check_port(loc_server_port): + websocket_helper.broadcast('send_start_reload', { + }) + else: + websocket_helper.broadcast('send_start_error', { + 'error': "We have detected port {} may not be open on the host network or a firewall is blocking it. Remote client connections to the server may be limited.".format(loc_server_port) + }) + else: + websocket_helper.broadcast('send_start_error', { + 'error': "We have detected the machine running Crafty has no connection to the internet. Client connections to the server may be limited." + }) + self.process = pexpect.spawn(self.server_command, cwd=self.server_path, timeout=None, encoding='utf-8') out_buf = ServerOutBuf(self.process, self.server_id) diff --git a/app/frontend/templates/base.html b/app/frontend/templates/base.html index 2e807ca7..9714b1e2 100644 --- a/app/frontend/templates/base.html +++ b/app/frontend/templates/base.html @@ -228,7 +228,12 @@ if (webSocket) { var x = document.querySelector('.modal-backdrop'); if(x){ x.remove()} - bootbox.alert(start_error.error); + bootbox.alert({ + message: start_error.error, + callback: function () { + location.reload(); + } +}) }); } diff --git a/main.py b/main.py index 9d9e258e..209fb2f7 100644 --- a/main.py +++ b/main.py @@ -131,6 +131,11 @@ if __name__ == '__main__': # this should always be last tasks_manager.start_main_kill_switch_watcher() + if not helper.check_internet(): + console.error("We have detected the machine running Crafty has no connection to the internet. Client connections to the server may be limited.") + elif not helper.check_port(helper.get_setting('https_port')): + console.error("We have detected Crafty's port, {} may not be open on the host network or a firewall is blocking it. Remote client connections to Crafty may be limited.".format(helper.get_setting('https_port'))) + Crafty = MainPrompt(tasks_manager, migration_manager) def sigterm_handler(signum, current_stack_frame): From 7316cc7ea0dbc734bf7dc58432c25db8abf23ea6 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 13 Sep 2021 15:03:47 -0400 Subject: [PATCH 2/2] Adds kill process button to dashboard. --- app/classes/shared/server.py | 3 ++ app/classes/web/ajax_handler.py | 13 +++++ app/frontend/templates/panel/dashboard.html | 60 +++++++++++++++++++-- app/translations/en_EN.json | 8 ++- app/translations/fi_FI.json | 8 ++- app/translations/fr_FR.json | 8 ++- 6 files changed, 93 insertions(+), 7 deletions(-) diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 49f463f9..0d23ec36 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -362,6 +362,9 @@ class Server: else: return False + def get_pid(self): + return self.PID + def detect_crash(self): logger.info("Detecting possible crash for server: {} ".format(self.name)) diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py index 12ffc318..1bc2977d 100644 --- a/app/classes/web/ajax_handler.py +++ b/app/classes/web/ajax_handler.py @@ -2,6 +2,7 @@ import json import logging import tempfile import threading +from typing import Container import zipfile import tornado.web @@ -197,6 +198,18 @@ class AjaxHandler(BaseHandler): self.redirect("/panel/server_detail?id={}&subpage=files".format(server_id)) return + elif page == "kill": + server_id = self.get_argument('id', None) + svr = self.controller.get_server_obj(server_id) + if svr.get_pid(): + try: + svr.killpid(svr.get_pid()) + except Exception as e: + logger.error("Could not find PID for requested termsig. Full error: {}".format(e)) + else: + logger.error("Could not find PID for requested termsig. Full error: {}".format(e)) + return + @tornado.web.authenticated def delete(self, page): if page == "del_file": diff --git a/app/frontend/templates/panel/dashboard.html b/app/frontend/templates/panel/dashboard.html index 223c0e9b..51cbd64f 100644 --- a/app/frontend/templates/panel/dashboard.html +++ b/app/frontend/templates/panel/dashboard.html @@ -126,13 +126,15 @@ {% if server['user_command_permission'] %} {% if server['stats']['running'] %} -   -   +   +   +   {% elif server['stats']['updating']%} UPDATING... {% else %} -   -   +   +   +   {% end %} {% end %} @@ -240,6 +242,25 @@ function send_command (server_id, command){ }); } +function send_kill (server_id){ + /* this getCookie function is in base.html */ + var token = getCookie("_xsrf"); + + $.ajax({ + type: "POST", + headers: {'X-XSRFToken': token}, + url: '/ajax/kill?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!') @@ -272,6 +293,37 @@ $( document ).ready(function() { title: '{% raw translate("dashboard", "sendingCommand") %}', message: '
  {% raw translate("dashboard", "bePatientRestart") %}
' }); + }); + $( ".kill_button" ).click(function() { + server_id = $(this).attr("data-id"); + bootbox.confirm({ + message: "This will kill the server process and all it's subprocesses. Killing a process can potentially corrupt files. Only do this in extreme circumstances. Are you sure you would like to continue?", + buttons: { + confirm: { + label: '{% raw translate("dashboard", "kill") %}', + className: 'btn-danger' + }, + cancel: { + label: '{% raw translate("panelConfig", "cancel") %}', + className: 'btn-secondary' + } + }, + callback: function (result) { + if(result){ + send_kill(server_id); + var dialog = bootbox.dialog({ + title: '{% raw translate("dashboard", "killing") %}', + message: '

Loading...

' +}); + +dialog.init(function(){ + setTimeout(function(){ + location.reload(); + }, 15000); +}); + } + } +}); }); if (webSocket) { cpu_data = document.getElementById('cpu_data'); diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index e30907a8..53940103 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -89,7 +89,13 @@ "sendingCommand": "Sending your command", "cpuCurFreq": "CPU Current Clock", "cpuMaxFreq": "CPU Maximum Clock", - "cpuCores": "CPU Cores" + "cpuCores": "CPU Cores", + "start": "Start", + "stop": "Stop", + "clone": "Clone", + "kill": "Kill Process", + "restart": "Restart", + "killing": "Killing process..." }, "accessDenied": { "accessDenied": "Access Denied", diff --git a/app/translations/fi_FI.json b/app/translations/fi_FI.json index 22fbfd2f..eece520e 100644 --- a/app/translations/fi_FI.json +++ b/app/translations/fi_FI.json @@ -89,7 +89,13 @@ "sendingCommand": "Lähetämme komentoasi", "cpuCurFreq": "Nykyinen kellotaajuus", "cpuMaxFreq": "Maksimi kellotaajuus", - "cpuCores": "Suorittimen ytimet" + "cpuCores": "Suorittimen ytimet", + "start": "Start", + "stop": "Stop", + "clone": "Clone", + "kill": "Kill Process", + "restart": "Restart", + "killing": "Killing process..." }, "accessDenied": { "accessDenied": "Käyttö estetty", diff --git a/app/translations/fr_FR.json b/app/translations/fr_FR.json index dd6f1c10..34888643 100644 --- a/app/translations/fr_FR.json +++ b/app/translations/fr_FR.json @@ -89,7 +89,13 @@ "sendingCommand": "Envoi de votre commande", "cpuCurFreq": "Fréquence CPU actuelle", "cpuMaxFreq": "Fréquence CPU Maximum", - "cpuCores": "Coeurs CPU" + "cpuCores": "Coeurs CPU", + "start": "Start", + "stop": "Stop", + "clone": "Clone", + "kill": "Kill Process", + "restart": "Restart", + "killing": "Killing process..." }, "accessDenied": { "accessDenied": "Accès Interdit",