Add unzip/dir request handler to api v2

This commit is contained in:
amcmanu3 2023-08-31 21:35:30 -04:00
parent d9e405e56c
commit 27f6c4c926
10 changed files with 161 additions and 66 deletions

View File

@ -325,3 +325,12 @@ class FileHelpers:
else: else:
return "false" return "false"
return return
def unzip_server(self, zip_path, user_id):
if Helpers.check_file_perms(zip_path):
temp_dir = tempfile.mkdtemp()
with zipfile.ZipFile(zip_path, "r") as zip_ref:
# extracts archive to temp directory
zip_ref.extractall(temp_dir)
if user_id:
return temp_dir

View File

@ -1135,17 +1135,6 @@ class Helpers:
</input></div><li>""" </input></div><li>"""
return output return output
def unzip_server(self, zip_path, user_id):
if Helpers.check_file_perms(zip_path):
temp_dir = tempfile.mkdtemp()
with zipfile.ZipFile(zip_path, "r") as zip_ref:
# extracts archive to temp directory
zip_ref.extractall(temp_dir)
if user_id:
self.websocket_helper.broadcast_user(
user_id, "send_temp_path", {"path": temp_dir}
)
@staticmethod @staticmethod
def unzip_backup_archive(backup_path, zip_name): def unzip_backup_archive(backup_path, zip_name):
zip_path = os.path.join(backup_path, zip_name) zip_path = os.path.join(backup_path, zip_name)

View File

@ -41,10 +41,10 @@ scheduler_intervals = {
class TasksManager: class TasksManager:
controller: Controller controller: Controller
def __init__(self, helper, controller): def __init__(self, helper, controller, file_helper):
self.helper: Helpers = helper self.helper: Helpers = helper
self.controller: Controller = controller self.controller: Controller = controller
self.tornado: Webserver = Webserver(helper, controller, self) self.tornado: Webserver = Webserver(helper, controller, self, file_helper)
try: try:
self.tz = get_localzone() self.tz = get_localzone()
except ZoneInfoNotFoundError as e: except ZoneInfoNotFoundError as e:

View File

@ -9,7 +9,7 @@ import bleach
import tornado.web import tornado.web
import tornado.escape import tornado.escape
from app.classes.shared.console import Console from app.classes.shared.file_helpers import FileHelpers
from app.classes.shared.helpers import Helpers from app.classes.shared.helpers import Helpers
from app.classes.shared.server import ServerOutBuf from app.classes.shared.server import ServerOutBuf
from app.classes.web.base_handler import BaseHandler from app.classes.web.base_handler import BaseHandler
@ -148,32 +148,6 @@ class AjaxHandler(BaseHandler):
self.controller.cached_login = "login_1.jpg" self.controller.cached_login = "login_1.jpg"
return return
elif page == "unzip_server":
path = urllib.parse.unquote(self.get_argument("path", ""))
if not path:
path = os.path.join(
self.controller.project_root,
"imports",
urllib.parse.unquote(self.get_argument("file", "")),
)
if Helpers.check_file_exists(path):
self.helper.unzip_server(path, exec_user["user_id"])
else:
user_id = exec_user["user_id"]
if user_id:
time.sleep(5)
user_lang = self.controller.users.get_user_lang_by_id(user_id)
self.helper.websocket_helper.broadcast_user(
user_id,
"send_start_error",
{
"error": self.helper.translation.translate(
"error", "no-file", user_lang
)
},
)
return
elif page == "jar_cache": elif page == "jar_cache":
if not superuser: if not superuser:
self.redirect("/panel/error?error=Not a super user") self.redirect("/panel/error?error=Not a super user")
@ -208,25 +182,3 @@ class AjaxHandler(BaseHandler):
new_dir = urllib.parse.unquote(self.get_argument("server_dir")) new_dir = urllib.parse.unquote(self.get_argument("server_dir"))
self.controller.update_master_server_dir(new_dir, exec_user["user_id"]) self.controller.update_master_server_dir(new_dir, exec_user["user_id"])
return return
def check_server_id(self, server_id, page_name):
if server_id is None:
logger.warning(
f"Server ID not defined in {page_name} ajax call ({server_id})"
)
Console.warning(
f"Server ID not defined in {page_name} ajax call ({server_id})"
)
return
server_id = bleach.clean(server_id)
# does this server id exist?
if not self.controller.servers.server_id_exists(server_id):
logger.warning(
f"Server ID not found in {page_name} ajax call ({server_id})"
)
Console.warning(
f"Server ID not found in {page_name} ajax call ({server_id})"
)
return
return True

View File

@ -8,6 +8,7 @@ import tornado.web
from app.classes.models.crafty_permissions import EnumPermissionsCrafty from app.classes.models.crafty_permissions import EnumPermissionsCrafty
from app.classes.models.users import ApiKeys from app.classes.models.users import ApiKeys
from app.classes.shared.helpers import Helpers from app.classes.shared.helpers import Helpers
from app.classes.shared.file_helpers import FileHelpers
from app.classes.shared.main_controller import Controller from app.classes.shared.main_controller import Controller
from app.classes.shared.translation import Translation from app.classes.shared.translation import Translation
from app.classes.models.management import DatabaseShortcuts from app.classes.models.management import DatabaseShortcuts
@ -24,15 +25,22 @@ class BaseHandler(tornado.web.RequestHandler):
helper: Helpers helper: Helpers
controller: Controller controller: Controller
translator: Translation translator: Translation
file_helper: FileHelpers
# noinspection PyAttributeOutsideInit # noinspection PyAttributeOutsideInit
def initialize( def initialize(
self, helper=None, controller=None, tasks_manager=None, translator=None self,
helper=None,
controller=None,
tasks_manager=None,
translator=None,
file_helper=None,
): ):
self.helper = helper self.helper = helper
self.controller = controller self.controller = controller
self.tasks_manager = tasks_manager self.tasks_manager = tasks_manager
self.translator = translator self.translator = translator
self.file_helper = file_helper
def set_default_headers(self) -> None: def set_default_headers(self) -> None:
""" """

View File

@ -51,6 +51,7 @@ from app.classes.web.routes.api.users.user.pfp import ApiUsersUserPfpHandler
from app.classes.web.routes.api.users.user.public import ApiUsersUserPublicHandler from app.classes.web.routes.api.users.user.public import ApiUsersUserPublicHandler
from app.classes.web.routes.api.crafty.config.index import ApiCraftyConfigIndexHandler from app.classes.web.routes.api.crafty.config.index import ApiCraftyConfigIndexHandler
from app.classes.web.routes.api.crafty.clogs.index import ApiCraftyLogIndexHandler from app.classes.web.routes.api.crafty.clogs.index import ApiCraftyLogIndexHandler
from app.classes.web.routes.api.crafty.imports.index import ApiImportFilesIndexHandler
def api_handlers(handler_args): def api_handlers(handler_args):
@ -76,6 +77,11 @@ def api_handlers(handler_args):
ApiCraftyLogIndexHandler, ApiCraftyLogIndexHandler,
handler_args, handler_args,
), ),
(
r"/api/v2/import/file/unzip/?",
ApiImportFilesIndexHandler,
handler_args,
),
# User routes # User routes
( (
r"/api/v2/users/?", r"/api/v2/users/?",

View File

@ -0,0 +1,123 @@
import os
import logging
import json
import html
from jsonschema import validate
from jsonschema.exceptions import ValidationError
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
from app.classes.shared.helpers import Helpers
from app.classes.web.base_api_handler import BaseApiHandler
logger = logging.getLogger(__name__)
files_get_schema = {
"type": "object",
"properties": {
"page": {"type": "string", "minLength": 1},
"folder": {"type": "string"},
"unzip": {"type": "boolean", "default": "True"},
},
"additionalProperties": False,
"minProperties": 1,
}
class ApiImportFilesIndexHandler(BaseApiHandler):
def post(self):
auth_data = self.authenticate_user()
if not auth_data:
return
if (
EnumPermissionsCrafty.SERVER_CREATION
not in self.controller.crafty_perms.get_crafty_permissions_list(
auth_data[4]["user_id"]
)
and not auth_data[4]["superuser"]
):
# if the user doesn't have Files or Backup permission, return an error
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
try:
data = json.loads(self.request.body)
except json.decoder.JSONDecodeError as e:
return self.finish_json(
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
)
try:
validate(data, files_get_schema)
except ValidationError as e:
return self.finish_json(
400,
{
"status": "error",
"error": "INVALID_JSON_SCHEMA",
"error_data": str(e),
},
)
# TODO: limit some columns for specific permissions?
folder = data["folder"]
user_id = auth_data[4]["user_id"]
root_path = False
if data["unzip"]:
if Helpers.check_file_exists(folder):
folder = self.file_helper.unzip_server(folder, user_id)
root_path = True
else:
if user_id:
user_lang = self.controller.users.get_user_lang_by_id(user_id)
self.helper.websocket_helper.broadcast_user(
user_id,
"send_start_error",
{
"error": self.helper.translation.translate(
"error", "no-file", user_lang
)
},
)
else:
if not self.helper.check_path_exists(folder):
if user_id:
user_lang = self.controller.users.get_user_lang_by_id(user_id)
self.helper.websocket_helper.broadcast_user(
user_id,
"send_start_error",
{
"error": self.helper.translation.translate(
"error", "no-file", user_lang
)
},
)
return_json = {
"root_path": {
"path": folder,
"top": root_path,
}
}
dir_list = []
unsorted_files = []
file_list = os.listdir(folder)
for item in file_list:
if os.path.isdir(os.path.join(folder, item)):
dir_list.append(item)
else:
unsorted_files.append(item)
file_list = sorted(dir_list, key=str.casefold) + sorted(
unsorted_files, key=str.casefold
)
for raw_filename in file_list:
filename = html.escape(raw_filename)
rel = os.path.join(folder, raw_filename)
dpath = os.path.join(folder, filename)
dpath = self.helper.wtol_path(dpath)
if os.path.isdir(rel):
return_json[filename] = {
"path": dpath,
"dir": True,
}
else:
return_json[filename] = {
"path": dpath,
"dir": False,
}
self.finish_json(200, {"status": "ok", "data": return_json})

View File

@ -48,13 +48,14 @@ class Webserver:
controller: Controller controller: Controller
helper: Helpers helper: Helpers
def __init__(self, helper, controller, tasks_manager): def __init__(self, helper, controller, tasks_manager, file_helper):
self.ioloop = None self.ioloop = None
self.http_server = None self.http_server = None
self.https_server = None self.https_server = None
self.helper = helper self.helper = helper
self.controller = controller self.controller = controller
self.tasks_manager = tasks_manager self.tasks_manager = tasks_manager
self.file_helper = file_helper
self._asyncio_patch() self._asyncio_patch()
@staticmethod @staticmethod
@ -146,6 +147,7 @@ class Webserver:
"controller": self.controller, "controller": self.controller,
"tasks_manager": self.tasks_manager, "tasks_manager": self.tasks_manager,
"translator": self.helper.translation, "translator": self.helper.translation,
"file_helper": self.file_helper,
} }
handlers = [ handlers = [
(r"/", DefaultHandler, handler_args), (r"/", DefaultHandler, handler_args),

View File

@ -18,12 +18,18 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
io_loop = None io_loop = None
def initialize( def initialize(
self, helper=None, controller=None, tasks_manager=None, translator=None self,
helper=None,
controller=None,
tasks_manager=None,
translator=None,
file_helper=None,
): ):
self.helper = helper self.helper = helper
self.controller = controller self.controller = controller
self.tasks_manager = tasks_manager self.tasks_manager = tasks_manager
self.translator = translator self.translator = translator
self.file_helper = file_helper
self.io_loop = tornado.ioloop.IOLoop.current() self.io_loop = tornado.ioloop.IOLoop.current()
def get_remote_ip(self): def get_remote_ip(self):

View File

@ -171,7 +171,7 @@ if __name__ == "__main__":
Console.info("Remote change complete.") Console.info("Remote change complete.")
import3 = Import3(helper, controller) import3 = Import3(helper, controller)
tasks_manager = TasksManager(helper, controller) tasks_manager = TasksManager(helper, controller, file_helper)
tasks_manager.start_webserver() tasks_manager.start_webserver()
def signal_handler(signum, _frame): def signal_handler(signum, _frame):