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:
return "false"
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>"""
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
def unzip_backup_archive(backup_path, zip_name):
zip_path = os.path.join(backup_path, zip_name)

View File

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

View File

@ -9,7 +9,7 @@ import bleach
import tornado.web
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.server import ServerOutBuf
from app.classes.web.base_handler import BaseHandler
@ -148,32 +148,6 @@ class AjaxHandler(BaseHandler):
self.controller.cached_login = "login_1.jpg"
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":
if not superuser:
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"))
self.controller.update_master_server_dir(new_dir, exec_user["user_id"])
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.users import ApiKeys
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.translation import Translation
from app.classes.models.management import DatabaseShortcuts
@ -24,15 +25,22 @@ class BaseHandler(tornado.web.RequestHandler):
helper: Helpers
controller: Controller
translator: Translation
file_helper: FileHelpers
# noinspection PyAttributeOutsideInit
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.controller = controller
self.tasks_manager = tasks_manager
self.translator = translator
self.file_helper = file_helper
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.crafty.config.index import ApiCraftyConfigIndexHandler
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):
@ -76,6 +77,11 @@ def api_handlers(handler_args):
ApiCraftyLogIndexHandler,
handler_args,
),
(
r"/api/v2/import/file/unzip/?",
ApiImportFilesIndexHandler,
handler_args,
),
# User routes
(
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
helper: Helpers
def __init__(self, helper, controller, tasks_manager):
def __init__(self, helper, controller, tasks_manager, file_helper):
self.ioloop = None
self.http_server = None
self.https_server = None
self.helper = helper
self.controller = controller
self.tasks_manager = tasks_manager
self.file_helper = file_helper
self._asyncio_patch()
@staticmethod
@ -146,6 +147,7 @@ class Webserver:
"controller": self.controller,
"tasks_manager": self.tasks_manager,
"translator": self.helper.translation,
"file_helper": self.file_helper,
}
handlers = [
(r"/", DefaultHandler, handler_args),

View File

@ -18,12 +18,18 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
io_loop = None
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.controller = controller
self.tasks_manager = tasks_manager
self.translator = translator
self.file_helper = file_helper
self.io_loop = tornado.ioloop.IOLoop.current()
def get_remote_ip(self):

View File

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