diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 53cfc3da..602ee8ab 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -1,4 +1,5 @@ import os +import re import sys import json import time @@ -8,7 +9,7 @@ import base64 import socket import random import logging -import configparser + from datetime import datetime from socket import gethostname @@ -125,6 +126,64 @@ class Helpers: pass return False + def log_colors(self, line): + # our regex replacements + # note these are in a tuple + + user_keywords = self.get_setting('keywords') + + replacements = [ + (r'(\[.+?/INFO\])', r'\1'), + (r'(\[.+?/WARN\])', r'\1'), + (r'(\[.+?/ERROR\])', r'\1'), + (r'(\w+?\[/\d+?\.\d+?\.\d+?\.\d+?\:\d+?\])', r'\1'), + (r'\[(\d\d:\d\d:\d\d)\]', r'[\1]'), + ] + + # highlight users keywords + for keyword in user_keywords: + search_replace = (r'({})'.format(keyword), r'\1') + replacements.append(search_replace) + + for old, new in replacements: + line = re.sub(old, new, line, flags=re.IGNORECASE) + + return line + + def tail_file(self, file_name, number_lines=20): + if not self.check_file_exists(file_name): + logger.warning("Unable to find file to tail: {}".format(file_name)) + return ["Unable to find file to tail: {}".format(file_name)] + + # length of lines is X char here + avg_line_length = 255 + + # create our buffer number - number of lines * avg_line_length + line_buffer = number_lines * avg_line_length + + # open our file + with open(file_name, 'r') as f: + + # seek + f.seek(0, 2) + + # get file size + fsize = f.tell() + + # set pos @ last n chars (buffer from above = number of lines * avg_line_length) + f.seek(max(fsize-line_buffer, 0), 0) + + # read file til the end + try: + lines = f.readlines() + + except Exception as e: + logger.warning('Unable to read a line in the file:{} - due to error: {}'.format(file_name, e)) + pass + + # now we are done getting the lines, let's return it + return lines + @staticmethod def check_writeable(path: str): filename = os.path.join(path, "tempfile.txt") @@ -334,7 +393,7 @@ class Helpers: cert.get_subject().CN = gethostname() cert.set_serial_number(1000) cert.gmtime_adj_notBefore(0) - cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60) + cert.gmtime_adj_notAfter(365 * 24 * 60 * 60) cert.set_issuer(cert.get_subject()) cert.set_pubkey(k) cert.sign(k, 'sha256') diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py new file mode 100644 index 00000000..ddaa3dcc --- /dev/null +++ b/app/classes/web/ajax_handler.py @@ -0,0 +1,72 @@ +import json +import logging +import tornado.web +import tornado.escape +import bleach + +from app.classes.shared.console import console +from app.classes.shared.models import Users, installer +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.helpers import helper + +logger = logging.getLogger(__name__) + + +class AjaxHandler(BaseHandler): + + def render_page(self, template, page_data): + self.render( + template, + data=page_data + ) + + @tornado.web.authenticated + def get(self, page): + user_data = json.loads(self.get_secure_cookie("user_data")) + error = bleach.clean(self.get_argument('error', "WTF Error!")) + + template = "panel/denied.html" + + page_data = { + 'user_data': user_data, + 'error': error + } + + if page == "error": + template = "public/error.html" + self.render_page(template, page_data) + + elif page == 'server_log': + server_id = self.get_argument('id', None) + + if server_id is None: + logger.warning("Server ID not found in server_log ajax call") + self.redirect("/panel/error?error=Server ID Not Found") + return False + + server_id = bleach.clean(server_id) + + server_data = db_helper.get_server_data_by_id(server_id) + if not server_data: + logger.warning("Server Data not found in server_log ajax call") + self.redirect("/panel/error?error=Server ID Not Found") + + if server_data['log_path']: + logger.warning("Server ID not found in server_log ajax call") + + log_lines = helper.get_setting('virtual_terminal_lines') + data = helper.tail_file(server_data['log_path'], log_lines) + + for d in data: + try: + line = helper.log_colors(d) + self.write('{}
'.format(line)) + # self.write(d.encode("utf-8")) + + except Exception as e: + logger.warning("Skipping Log Line due to error: {}".format(e)) + pass + + diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 0006bec6..14320121 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -101,7 +101,7 @@ class PublicHandler(BaseHandler): }).where(Users.username == entered_username).execute() # log this login - db_helper.add_to_audit_log(user_data.user_id, "Logged in", None, self.get_remote_ip()) + db_helper.add_to_audit_log(user_data.user_id, "Logged in", 0, self.get_remote_ip()) cookie_data = { "username": user_data.username, diff --git a/app/classes/web/tornado.py b/app/classes/web/tornado.py index f79ca01b..6753b4f9 100644 --- a/app/classes/web/tornado.py +++ b/app/classes/web/tornado.py @@ -22,6 +22,7 @@ try: from app.classes.web.panel_handler import PanelHandler from app.classes.web.default_handler import DefaultHandler from app.classes.web.server_handler import ServerHandler + from app.classes.web.ajax_handler import AjaxHandler except ModuleNotFoundError as e: logger.critical("Import Error: Unable to load {} module".format(e, e.name)) @@ -120,6 +121,7 @@ class webserver: (r'/public/(.*)', PublicHandler), (r'/panel/(.*)', PanelHandler), (r'/server/(.*)', ServerHandler), + (r'/ajax/(.*)', AjaxHandler), ] app = tornado.web.Application( diff --git a/app/frontend/static/assets/css/crafty.css b/app/frontend/static/assets/css/crafty.css index 4ef0777f..dad6cbc6 100644 --- a/app/frontend/static/assets/css/crafty.css +++ b/app/frontend/static/assets/css/crafty.css @@ -35,4 +35,28 @@ .sidebar > .nav .nav-item .nav-link, .collapsed{ padding: 15px 30px; +} + +.mc-log-time{ + color:#19d895; +} + +.mc-log-info{ + color:#8862e0; +} + +.mc-log-warn{ + color:#ffaf00; +} + +.mc-log-error{ + color:#ff6258; +} + +.mc-log-keyword{ + color:#2196f3; +} + +.scrollable-element { + scrollbar-color: red yellow; } \ No newline at end of file diff --git a/app/frontend/templates/panel/dashboard.html b/app/frontend/templates/panel/dashboard.html index c7fef96a..1a05f462 100644 --- a/app/frontend/templates/panel/dashboard.html +++ b/app/frontend/templates/panel/dashboard.html @@ -132,6 +132,7 @@ {% else %}   {% end %} + diff --git a/app/frontend/templates/panel/server_term.html b/app/frontend/templates/panel/server_term.html index a74f85ff..9ceaa458 100644 --- a/app/frontend/templates/panel/server_term.html +++ b/app/frontend/templates/panel/server_term.html @@ -24,14 +24,58 @@ +
+ +
+
+
+
+
+ Server Status: + {% if data['server_stats'][0]['running'] %} + Online
+ Server Started: {{ data['server_stats'][0]['started'] }} + {% else %} + Offline
+ Server Started: Not Started + {% end %} +
+ +
+ CPU: {{ data['server_stats'][0]['cpu'] }}%
+ Mem: {{ data['server_stats'][0]['mem'] }}
+ {% if data['server_stats'][0]['int_ping_results'] %} + Players: {{ data['server_stats'][0]['online'] }} / {{ data['server_stats'][0]['max'] }}
+ {% else %} + Players: 0/0
+ {% end %} +
+ +
+ {% if data['server_stats'][0]['version'] != 'False' %} + Server: {{ data['server_stats'][0]['version'] }}
+ Desc: {{ data['server_stats'][0]['desc'] }}
+ {% else %} + Server: Unable To Connect
+ Desc: Unable To Connect
+ {% end %} +
+ +
+
+ +
+
+ +
+
-
-
+
+
+
+
+
+ +
+ + + + +
+
@@ -89,11 +139,85 @@ {% block js %} {% end %} \ No newline at end of file diff --git a/config.json b/config.json index 091457db..1b3355cd 100644 --- a/config.json +++ b/config.json @@ -10,5 +10,7 @@ "stats_update_frequency": 60, "max_stats_count": 1000, "delete_default_json": false, - "show_contribute_link": true + "show_contribute_link": true, + "virtual_terminal_lines": 100, + "keywords": ["help", "chunk"] } \ No newline at end of file