diff --git a/imagemonk/resources/image.py b/imagemonk/resources/image.py index 9fedb06..f1253e4 100644 --- a/imagemonk/resources/image.py +++ b/imagemonk/resources/image.py @@ -1,20 +1,117 @@ +import hashlib +import shutil +import uuid + +import flask + +from imagemonk import constants +from imagemonk import database +from imagemonk import exceptions from imagemonk.resources._shared import ImageMonkResource class ImageUpload(ImageMonkResource): - route = ("/image/",) + routes = ("/image/",) def post(self): - raise NotImplementedError + if "image" not in flask.request.files: + raise + + uploaded = flask.request.files["image"] + + breakpoint() + + if not uploaded.filename: + raise + + format = uploaded.filename.rpartition(".")[-1].lower() + + if format not in flask.current_app.appconfig.upload.formats: + raise + + image = database.ImageRecord(format=format, width=0, height=0, owner="foobar") + + imagedir = flask.current_app.appconfig.storage_path / str(image.uuid) + + imagedir.mkdir() + + uploaded.save(imagedir / f"base.{format}") + + with (imagedir / f"base.{format}").open() as infile: + image.sha256 = hashlib.sha256(infile.read()).hexdigest() + + with database.interface.atomic(): + image.save() + + return None, 201 class Image(ImageMonkResource): - route = ("/image/.jpg",) + routes = ("/image/.jpeg",) def get(self, image_id: str): - raise NotImplementedError - def delete(self, image_id: str): - raise NotImplementedError + image = database.ImageRecord.get( + database.ImageRecord.uuid + == uuid.UUID(image_id) & database.ImageRecord.format + == format + ) + + if image.deleted: + raise exceptions.ImageResourceDeletedError( + f"Image with ID '{image_id}' was deleted" + ) + + filepath = ( + flask.current_app.appconfig.storage_path + / str(image.uuid) + / f"base.{image.format}" + ) + + if not filepath.exists(): + with database.interface.atomic(): + image.deleted = True + image.save() + raise exceptions.ImageFileRemovedError( + f"Image file with ID '{image_id}' removed from the server" + ) + + flask.send_file( + filepath, + mimetype=f"image/{'jpeg' if image.format == 'jpg' else image.format}", + # images are indexed by UUID with no ability to update, y'all should cache + # this thing 'till the sun explodes + cache_timeout=(60 * 60 * 24 * 365), + ) + + return ( + None, + 200, + {constants.HTTP_HEADER_RESPONSE_DIGEST: f"sha-256={image.sha256}"}, + ) + + def delete(self, image_id: str, format: str): + + image = database.ImageRecord.get( + database.ImageRecord.uuid + == uuid.UUID(image_id) & database.ImageRecord.format + == format + ) + + if image.deleted: + raise exceptions.ImageResourceDeletedError( + f"Image with ID '{image_id}' was deleted" + ) + + filepath = flask.current_app.appconfig.storage_path / str(image.uuid) + + with database.interface.atomic(): + image.deleted = True + image.save() + + if filepath.exists(): + shutil.rmtree(filepath) + + return None, 204