diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..a2f64789 Binary files /dev/null and b/.DS_Store differ diff --git a/app/classes/shared/controller.py b/app/classes/shared/controller.py index b92fb2f2..b36e53d5 100644 --- a/app/classes/shared/controller.py +++ b/app/classes/shared/controller.py @@ -306,7 +306,10 @@ class Controller: tempDir = tempfile.mkdtemp() with zipfile.ZipFile(zip_path, 'r') as zip_ref: zip_ref.extractall(tempDir) - test = zip_ref.filelist[1].filename + for i in range(len(zip_ref.filelist)): + if len(zip_ref.filelist) > 1 or not zip_ref.filelist[i].filename.endswith('/'): + test = zip_ref.filelist[i].filename + break path_list = test.split('/') root_path = path_list[0] if len(path_list) > 1: diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 424d89ff..19e6504c 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -2,6 +2,7 @@ import os import re import sys import json +import tempfile import time import uuid import string @@ -258,6 +259,44 @@ class Helpers: logger.critical("Unable to write to {} - Error: {}".format(path, e)) return False + def unzipFile(self, zip_path): + new_dir_list = zip_path.split('/') + new_dir = '' + for i in range(len(new_dir_list)-1): + if i == 0: + new_dir += new_dir_list[i] + else: + new_dir += '/'+new_dir_list[i] + + if helper.check_file_perms(zip_path) and os.path.isfile(zip_path): + helper.ensure_dir_exists(new_dir) + tempDir = tempfile.mkdtemp() + try: + with zipfile.ZipFile(zip_path, 'r') as zip_ref: + zip_ref.extractall(tempDir) + for i in range(len(zip_ref.filelist)): + if len(zip_ref.filelist) > 1 or not zip_ref.filelist[i].filename.endswith('/'): + test = zip_ref.filelist[i].filename + break + path_list = test.split('/') + root_path = path_list[0] + if len(path_list) > 1: + for i in range(len(path_list) - 2): + root_path = os.path.join(root_path, path_list[i + 1]) + + full_root_path = os.path.join(tempDir, root_path) + + for item in os.listdir(full_root_path): + try: + shutil.move(os.path.join(full_root_path, item), os.path.join(new_dir, item)) + except Exception as ex: + logger.error('ERROR IN ZIP IMPORT: {}'.format(ex)) + except Exception as ex: + print(ex) + else: + return "false" + return + def ensure_logging_setup(self): log_file = os.path.join(os.path.curdir, 'logs', 'commander.log') session_log_file = os.path.join(os.path.curdir, 'logs', 'session.log') @@ -559,14 +598,6 @@ class Helpers: return json.loads(content) @staticmethod - def zip_directory(file, path, compression=zipfile.ZIP_LZMA): - with zipfile.ZipFile(file, 'w', compression) as zf: - for root, dirs, files in os.walk(path): - for file in files: - zf.write(os.path.join(root, file), - os.path.relpath(os.path.join(root, file), - os.path.join(path, '..'))) - @staticmethod def copy_files(source, dest): if os.path.isfile(source): shutil.copyfile(source, dest) diff --git a/app/classes/shared/models.py b/app/classes/shared/models.py index e13ab00d..1081939a 100644 --- a/app/classes/shared/models.py +++ b/app/classes/shared/models.py @@ -321,7 +321,6 @@ class db_shortcuts: def get_all_authorized_servers(user_id): user_servers = User_Servers.select().where(User_Servers.user_id == user_id) user_roles = User_Roles.select().where(User_Roles.user_id == user_id) - server_data = [] roles_list = [] role_server = [] server_data = [] @@ -596,6 +595,11 @@ class db_shortcuts: def add_user_server(server_id, user_id, us_permissions): servers = User_Servers.insert({User_Servers.server_id: server_id, User_Servers.user_id: user_id, User_Servers.permissions: us_permissions}).execute() return servers + + @staticmethod + def add_role_server(server_id, role_id): + servers = Role_Servers.insert({Role_Servers.server_id: server_id, Role_Servers.role_id: role_id}).execute() + return servers @staticmethod diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 8ddc10c0..6baa8713 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -11,6 +11,8 @@ import schedule import logging.config import zipfile from threading import Thread +import shutil +import zlib import html @@ -416,17 +418,15 @@ class Server: self.is_backingup = True conf = db_helper.get_backup_config(self.server_id) try: - backup_filename = "{}/{}.zip".format(self.settings['backup_path'], datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')) + backup_filename = "{}/{}".format(self.settings['backup_path'], datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')) logger.info("Creating backup of server '{}' (ID#{}) at '{}'".format(self.settings['server_name'], self.server_id, backup_filename)) - helper.zip_directory(backup_filename, self.server_path) - while len(self.list_backups()) > conf["max_backups"]: + shutil.make_archive(backup_filename, 'zip', self.server_path) + while len(self.list_backups()) > conf["max_backups"] and conf["max_backups"] > 0: backup_list = self.list_backups() oldfile = backup_list[0] - backup_path = self.settings['backup_path'] - old_file_name = oldfile['path'] - back_old_file = os.path.join(backup_path, old_file_name) - logger.info("Removing old backup '{}'".format(oldfile)) - os.remove(back_old_file) + oldfile_path = "{}/{}".format(conf['backup_path'], oldfile['path']) + logger.info("Removing old backup '{}'".format(oldfile['path'])) + os.remove(oldfile_path) self.is_backingup = False logger.info("Backup of server: {} completed".format(self.name)) return @@ -503,6 +503,7 @@ class Server: while db_helper.get_server_stats_by_id(self.server_id)['updating']: if downloaded and not self.is_backingup: + print("Backup Status: " + str(self.is_backingup)) logger.info("Executable updated successfully. Starting Server") db_helper.set_update(self.server_id, False) diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index 572918db..91d8ff0b 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -113,16 +113,6 @@ class TasksManager: elif command == "update_executable": svr.jar_update() - elif command == "delete_server": - logger.info( - "Removing server from panel for server: {}".format(c['server_id']['server_name'])) - self.controller.remove_server(c['server_id']['server_id'], False) - - elif command == "delete_server_files": - logger.info( - "Removing server and all associated files for server: {}".format(c['server_id']['server_name'])) - self.controller.remove_server(c['server_id']['server_id'], True) - db_helper.mark_command_complete(c.get('command_id', None)) time.sleep(1) diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py index bca9397e..f051c956 100644 --- a/app/classes/web/ajax_handler.py +++ b/app/classes/web/ajax_handler.py @@ -1,5 +1,9 @@ import json import logging +import tempfile +import threading +import zipfile + import tornado.web import tornado.escape import bleach @@ -183,10 +187,58 @@ class AjaxHandler(BaseHandler): logger.warning("Invalid path in create_dir ajax call ({})".format(dir_path)) console.warning("Invalid path in create_dir ajax call ({})".format(dir_path)) return - # Create the directory os.mkdir(dir_path) + elif page == "unzip_file": + server_id = self.get_argument('id', None) + path = self.get_argument('path', None) + helper.unzipFile(path) + self.redirect("/panel/server_detail?id={}&subpage=files".format(server_id)) + return + + elif page == "upload_files": + server_id = self.get_argument('id', None) + path = self.get_argument('path', None) + files = self.request.files['files'] + upload_thread = threading.Thread(target=self.do_upload, daemon=True, name=files[0]['filename'], + args=(server_id, path, files)) + upload_thread.start() + self.redirect("/panel/server_detail?id={}&subpage=files".format(server_id)) + + def do_upload(self, server_id, path, files): + if helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], path): + try: + for file in files: + if file['filename'].split('.') is not None: + self._upload_file(file['body'], path, file['filename']) + else: + logger.error("Directory Detected. Skipping") + except Exception as e: + logger.error("Error while uploading files: {}".format(e)) + else: + logger.error("Invalid directory requested. Canceling upload") + return + + def _upload_file(self, file_data, file_path, file_name): + error = "" + + file_full_path = os.path.join(file_path, file_name) + if os.path.exists(file_full_path): + error = "A file with this name already exists." + + if not helper.check_writeable(file_path): + error = "Unwritable Path" + + if error != "": + logger.error("Unable to save uploaded file due to: {}".format(error)) + return False + + output_file = open(file_full_path, 'wb') + output_file.write(file_data) + logger.info('Saving File: {}'.format(file_full_path)) + return True + @tornado.web.authenticated def delete(self, page): if page == "del_file": @@ -229,6 +281,18 @@ class AjaxHandler(BaseHandler): # os.rmdir(dir_path) # Would only remove empty directories shutil.rmtree(dir_path) # Removes also when there are contents + elif page == "delete_server": + server_id = self.get_argument('id', None) + logger.info( + "Removing server from panel for server: {}".format(db_helper.get_server_friendly_name(server_id))) + self.controller.remove_server(server_id, False) + + elif page == "delete_server_files": + server_id = self.get_argument('id', None) + logger.info( + "Removing server and all associated files for server: {}".format(db_helper.get_server_friendly_name(server_id))) + self.controller.remove_server(server_id, True) + @tornado.web.authenticated def put(self, page): if page == "save_file": diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index a0963ba7..0a77a194 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -338,17 +338,9 @@ class PanelHandler(BaseHandler): elif page == "edit_user": user_id = self.get_argument('id', None) - user_servers = db_helper.get_authorized_servers(user_id) role_servers = db_helper.get_authorized_servers_from_roles(user_id) page_role_servers = [] servers = set() - for server in user_servers: - flag = False - for rserver in role_servers: - if rserver['server_id'] == server['server_id']: - flag = True - if not flag: - servers.add(server['server_id']) for server in role_servers: page_role_servers.append(server['server_id']) page_data['new_user'] = False @@ -657,7 +649,6 @@ class PanelHandler(BaseHandler): "enabled": enabled, "regen_api": regen_api, "roles": roles, - "servers": servers, } db_helper.update_user(user_id, user_data=user_data) @@ -713,7 +704,6 @@ class PanelHandler(BaseHandler): user_id = db_helper.add_user(username, password=password0, enabled=enabled) user_data = { "roles": roles, - "servers": servers, } db_helper.update_user(user_id, user_data) diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index 595e9ef1..1d65c35d 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -195,8 +195,11 @@ class ServerHandler(BaseHandler): return server_type, server_version = server_parts # todo: add server type check here and call the correct server add functions if not a jar + role_ids = db_helper.get_user_roles_id(exec_user_id) new_server_id = self.controller.create_jar_server(server_type, server_version, server_name, min_mem, max_mem, port) db_helper.add_user_server(new_server_id, exec_user_id, "11111111") + for role in role_ids: + db_helper.add_role_server(new_server_id, role) db_helper.add_to_audit_log(exec_user_data['user_id'], "created a {} {} server named \"{}\"".format(server_version, str(server_type).capitalize(), server_name), # Example: Admin created a 1.16.5 Bukkit server named "survival" new_server_id, diff --git a/app/frontend/static/assets/css/crafty.css b/app/frontend/static/assets/css/crafty.css index ea94ad5c..892bd52f 100644 --- a/app/frontend/static/assets/css/crafty.css +++ b/app/frontend/static/assets/css/crafty.css @@ -79,4 +79,4 @@ body { background-color: var(--dark) !important; /* Firefox */ } .actions_serverlist > a > i { cursor: pointer; -} \ No newline at end of file +} diff --git a/app/frontend/static/assets/css/shared/style.css b/app/frontend/static/assets/css/shared/style.css index a7093450..35a40ab2 100755 --- a/app/frontend/static/assets/css/shared/style.css +++ b/app/frontend/static/assets/css/shared/style.css @@ -16188,41 +16188,35 @@ body.avgrund-active { border-radius: 2px; } /* Context Menu */ -.context-menu-icon:before { - color: #000; - font: normal normal normal 15px/1 "Material Design Icons"; } + .context-menu { + position: absolute; + text-align: center; + background: lightgray; + border: 1px solid black; + display: none; + } -.context-menu-icon.context-menu-icon-cut:before { - content: '\F190'; } + .context-menu ul { + padding: 0px; + margin: 0px; + min-width: 150px; + list-style: none; + } -.context-menu-icon.context-menu-icon-edit:before { - content: '\F3EF'; } + .context-menu ul li { + padding-bottom: 7px; + padding-top: 7px; + border: 1px solid black; + } -.context-menu-icon.context-menu-icon-copy:before { - content: '\F18F'; } - -.context-menu-icon.context-menu-icon-paste:before { - content: '\F613'; } - -.context-menu-icon.context-menu-icon-delete:before { - content: '\F6CB'; } - -.context-menu-icon.context-menu-icon-quit:before { - content: '\F156'; } - -.context-menu-list { - -webkit-box-shadow: none; - box-shadow: none; - border: 1px solid #dee2e6; } - .context-menu-list .context-menu-item span { - color: #000; - font-size: 0.75rem; - font-family: "roboto", sans-serif; } - .context-menu-list .context-menu-item.context-menu-hover { - background: #000; } - .context-menu-list .context-menu-item.context-menu-hover span { - color: #fff; } + .context-menu ul li a { + text-decoration: none; + color: black; + } + .context-menu ul li:hover { + background: darkgray; + } /* Clockpicker */ .clockpicker-popover { background-color: #dee2e6; } diff --git a/app/frontend/templates/panel/panel_edit_user.html b/app/frontend/templates/panel/panel_edit_user.html index 3b273461..c9a7ff33 100644 --- a/app/frontend/templates/panel/panel_edit_user.html +++ b/app/frontend/templates/panel/panel_edit_user.html @@ -121,12 +121,10 @@