file_manager: handle file upload processing
This implentation allows for uploads to different local paths by specifying a "root" argument in the form. Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
2ae4034b97
commit
f0fa1295a7
|
@ -202,10 +202,8 @@ class MoonrakerApp:
|
||||||
'path': file_path, 'methods': methods, 'op_check_cb': op_check_cb}
|
'path': file_path, 'methods': methods, 'op_check_cb': op_check_cb}
|
||||||
self.mutable_router.add_handler(pattern, FileRequestHandler, params)
|
self.mutable_router.add_handler(pattern, FileRequestHandler, params)
|
||||||
|
|
||||||
def register_upload_handler(self, pattern, upload_path, op_check_cb=None):
|
def register_upload_handler(self, pattern):
|
||||||
params = {
|
params = {'server': self.server, 'auth': self.auth}
|
||||||
'server': self.server, 'auth': self.auth,
|
|
||||||
'path': upload_path, 'op_check_cb': op_check_cb}
|
|
||||||
self.mutable_router.add_handler(pattern, FileUploadHandler, params)
|
self.mutable_router.add_handler(pattern, FileUploadHandler, params)
|
||||||
|
|
||||||
def remove_handler(self, endpoint):
|
def remove_handler(self, endpoint):
|
||||||
|
@ -345,70 +343,18 @@ class FileRequestHandler(AuthorizedFileHandler):
|
||||||
self.finish({'result': filename})
|
self.finish({'result': filename})
|
||||||
|
|
||||||
class FileUploadHandler(AuthorizedRequestHandler):
|
class FileUploadHandler(AuthorizedRequestHandler):
|
||||||
def initialize(self, server, auth, path, op_check_cb=None,):
|
def initialize(self, server, auth):
|
||||||
super(FileUploadHandler, self).initialize(server, auth)
|
super(FileUploadHandler, self).initialize(server, auth)
|
||||||
self.op_check_cb = op_check_cb
|
|
||||||
self.file_path = path
|
|
||||||
|
|
||||||
async def post(self):
|
async def post(self):
|
||||||
start_print = False
|
file_manager = self.server.lookup_plugin('file_manager')
|
||||||
dir_path = ""
|
|
||||||
print_args = self.request.arguments.get('print', [])
|
|
||||||
path_args = self.request.arguments.get('path', [])
|
|
||||||
if print_args:
|
|
||||||
start_print = print_args[0].decode().lower() == "true"
|
|
||||||
if path_args:
|
|
||||||
dir_path = path_args[0].decode().lstrip("/")
|
|
||||||
upload = self.get_file()
|
|
||||||
filename = "_".join(upload['filename'].strip().split()).lstrip("/")
|
|
||||||
if dir_path:
|
|
||||||
filename = os.path.join(dir_path, filename)
|
|
||||||
full_path = os.path.join(self.file_path, filename)
|
|
||||||
# Make sure the file isn't currently loaded
|
|
||||||
ongoing = False
|
|
||||||
if self.op_check_cb is not None:
|
|
||||||
try:
|
|
||||||
ongoing = await self.op_check_cb(full_path)
|
|
||||||
except ServerError as e:
|
|
||||||
if e.status_code == 403:
|
|
||||||
raise tornado.web.HTTPError(
|
|
||||||
403, "File is loaded, upload not permitted")
|
|
||||||
else:
|
|
||||||
# Couldn't reach Klippy, so it should be safe
|
|
||||||
# to permit the upload but not start
|
|
||||||
start_print = False
|
|
||||||
|
|
||||||
# Don't start if another print is currently in progress
|
|
||||||
start_print = start_print and not ongoing
|
|
||||||
try:
|
try:
|
||||||
if dir_path:
|
result = await file_manager.process_file_upload(self.request)
|
||||||
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
except ServerError as e:
|
||||||
with open(full_path, 'wb') as fh:
|
raise tornado.web.HTTPError(
|
||||||
fh.write(upload['body'])
|
e.status_code, str(e))
|
||||||
self.server.notify_filelist_changed(filename, 'added')
|
self.finish(result)
|
||||||
except Exception:
|
|
||||||
raise tornado.web.HTTPError(500, "Unable to save file")
|
|
||||||
if start_print:
|
|
||||||
# Make a Klippy Request to "Start Print"
|
|
||||||
gcode_apis = self.server.lookup_plugin('gcode_apis')
|
|
||||||
try:
|
|
||||||
await gcode_apis.gcode_start_print(
|
|
||||||
self.request.path, 'POST', {'filename': filename})
|
|
||||||
except ServerError:
|
|
||||||
# Attempt to start print failed
|
|
||||||
start_print = False
|
|
||||||
self.finish({'result': filename, 'print_started': start_print})
|
|
||||||
|
|
||||||
def get_file(self):
|
|
||||||
# File uploads must have a single file request
|
|
||||||
if len(self.request.files) != 1:
|
|
||||||
raise tornado.web.HTTPError(
|
|
||||||
400, "Bad Request, can only process a single file upload")
|
|
||||||
f_list = list(self.request.files.values())[0]
|
|
||||||
if len(f_list) != 1:
|
|
||||||
raise tornado.web.HTTPError(
|
|
||||||
400, "Bad Request, can only process a single file upload")
|
|
||||||
return f_list[0]
|
|
||||||
|
|
||||||
class EmulateOctoprintHandler(AuthorizedRequestHandler):
|
class EmulateOctoprintHandler(AuthorizedRequestHandler):
|
||||||
def get(self):
|
def get(self):
|
||||||
|
|
|
@ -38,17 +38,14 @@ class FileManager:
|
||||||
self.server.register_endpoint(
|
self.server.register_endpoint(
|
||||||
"/server/files/copy", "file_copy", ['POST'],
|
"/server/files/copy", "file_copy", ['POST'],
|
||||||
self._handle_file_move_copy)
|
self._handle_file_move_copy)
|
||||||
|
# Register APIs to handle file uploads
|
||||||
|
self.server.register_upload_handler("/server/files/upload")
|
||||||
|
self.server.register_upload_handler("/api/files/local")
|
||||||
|
|
||||||
def _register_static_files(self, gcode_path):
|
def _register_static_files(self, gcode_path):
|
||||||
self.server.register_static_file_handler(
|
self.server.register_static_file_handler(
|
||||||
'/server/files/gcodes/', gcode_path, can_delete=True,
|
'/server/files/gcodes/', gcode_path, can_delete=True,
|
||||||
op_check_cb=self._handle_operation_check)
|
op_check_cb=self._handle_operation_check)
|
||||||
self.server.register_upload_handler(
|
|
||||||
'/server/files/upload', gcode_path,
|
|
||||||
op_check_cb=self._handle_operation_check)
|
|
||||||
self.server.register_upload_handler(
|
|
||||||
'/api/files/local', gcode_path,
|
|
||||||
op_check_cb=self._handle_operation_check)
|
|
||||||
|
|
||||||
def load_config(self, config):
|
def load_config(self, config):
|
||||||
sd = config.get('sd_path', None)
|
sd = config.get('sd_path', None)
|
||||||
|
@ -156,7 +153,8 @@ class FileManager:
|
||||||
if source is None:
|
if source is None:
|
||||||
raise self.server.error("File move/copy request issing source")
|
raise self.server.error("File move/copy request issing source")
|
||||||
if destination is None:
|
if destination is None:
|
||||||
raise self.server.error("File move/copy request missing destination")
|
raise self.server.error(
|
||||||
|
"File move/copy request missing destination")
|
||||||
source_base, source_path = self._convert_path(source)
|
source_base, source_path = self._convert_path(source)
|
||||||
dest_base, dest_path = self._convert_path(destination)
|
dest_base, dest_path = self._convert_path(destination)
|
||||||
if source_base != "gcodes" or dest_base != "gcodes":
|
if source_base != "gcodes" or dest_base != "gcodes":
|
||||||
|
@ -168,6 +166,7 @@ class FileManager:
|
||||||
# make sure the destination is not in use
|
# make sure the destination is not in use
|
||||||
if os.path.exists(dest_path):
|
if os.path.exists(dest_path):
|
||||||
await self._handle_operation_check(dest_path)
|
await self._handle_operation_check(dest_path)
|
||||||
|
action = ""
|
||||||
if path == "/server/files/move":
|
if path == "/server/files/move":
|
||||||
# if moving the file, make sure the source is not in use
|
# if moving the file, make sure the source is not in use
|
||||||
await self._handle_operation_check(source_path)
|
await self._handle_operation_check(source_path)
|
||||||
|
@ -273,6 +272,74 @@ class FileManager:
|
||||||
ioloop.spawn_callback(self._update_metadata)
|
ioloop.spawn_callback(self._update_metadata)
|
||||||
return dict(new_list)
|
return dict(new_list)
|
||||||
|
|
||||||
|
async def process_file_upload(self, request):
|
||||||
|
start_print = print_ongoing = False
|
||||||
|
dir_path = ""
|
||||||
|
# lookup root file path
|
||||||
|
root_args = request.arguments.get('root', ['gcodes'])
|
||||||
|
root = root_args[0].strip()
|
||||||
|
file_path = self.file_paths.get(root, None)
|
||||||
|
if file_path is None:
|
||||||
|
raise self.server.error(400, "Unknown root path")
|
||||||
|
# check relative path
|
||||||
|
path_args = request.arguments.get('path', [])
|
||||||
|
if path_args:
|
||||||
|
dir_path = path_args[0].decode().lstrip("/")
|
||||||
|
# check if print should be started after a "gcodes" upload
|
||||||
|
if root == "gcodes":
|
||||||
|
print_args = request.arguments.get('print', [])
|
||||||
|
if print_args:
|
||||||
|
start_print = print_args[0].decode().lower() == "true"
|
||||||
|
# fetch the upload from the request
|
||||||
|
if len(request.files) != 1:
|
||||||
|
raise self.server.error(
|
||||||
|
400, "Bad Request, can only process a single file upload")
|
||||||
|
f_list = list(request.files.values())[0]
|
||||||
|
if len(f_list) != 1:
|
||||||
|
raise self.server.error(
|
||||||
|
400, "Bad Request, can only process a single file upload")
|
||||||
|
upload = f_list[0]
|
||||||
|
filename = "_".join(upload['filename'].strip().split()).lstrip("/")
|
||||||
|
if dir_path:
|
||||||
|
filename = os.path.join(dir_path, filename)
|
||||||
|
full_path = os.path.join(file_path, filename)
|
||||||
|
# Verify that the operation can be done if attempting to upload a gcode
|
||||||
|
if root == 'gcodes':
|
||||||
|
try:
|
||||||
|
print_ongoing = await self._handle_operation_check(full_path)
|
||||||
|
except self.server.error as e:
|
||||||
|
if e.status_code == 403:
|
||||||
|
raise self.server.error(
|
||||||
|
403, "File is loaded, upload not permitted")
|
||||||
|
else:
|
||||||
|
# Couldn't reach Klippy, so it should be safe
|
||||||
|
# to permit the upload but not start
|
||||||
|
start_print = False
|
||||||
|
|
||||||
|
# Don't start if another print is currently in progress
|
||||||
|
start_print = start_print and not print_ongoing
|
||||||
|
try:
|
||||||
|
if dir_path:
|
||||||
|
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
||||||
|
with open(full_path, 'wb') as fh:
|
||||||
|
fh.write(upload['body'])
|
||||||
|
except Exception:
|
||||||
|
raise self.server.error(500, "Unable to save file")
|
||||||
|
if start_print:
|
||||||
|
# Make a Klippy Request to "Start Print"
|
||||||
|
gcode_apis = self.server.lookup_plugin('gcode_apis')
|
||||||
|
try:
|
||||||
|
await gcode_apis.gcode_start_print(
|
||||||
|
request.path, 'POST', {'filename': filename})
|
||||||
|
except self.server.error:
|
||||||
|
# Attempt to start print failed
|
||||||
|
start_print = False
|
||||||
|
if root == 'gcodes':
|
||||||
|
self.server.notify_filelist_changed(filename, 'added')
|
||||||
|
return {'result': filename, 'print_started': start_print}
|
||||||
|
else:
|
||||||
|
return {'result': filename}
|
||||||
|
|
||||||
def get_file_list(self, format_list=False, base='gcodes'):
|
def get_file_list(self, format_list=False, base='gcodes'):
|
||||||
try:
|
try:
|
||||||
filelist = self._update_file_list(base)
|
filelist = self._update_file_list(base)
|
||||||
|
|
Loading…
Reference in New Issue