diff --git a/foxfiles/blueprints/api/files.py b/foxfiles/blueprints/api/files.py index ae5256d..c7324ab 100644 --- a/foxfiles/blueprints/api/files.py +++ b/foxfiles/blueprints/api/files.py @@ -7,6 +7,8 @@ from sqlalchemy import select from sqlalchemy.orm import Session from foxfiles.db import File, engine +from foxfiles.errors import Errors +from foxfiles.files import delete_file from foxfiles.user import maybe_token, require_user from foxfiles.settings import BASE_URL @@ -33,7 +35,9 @@ class ListFilesResponse(BaseModel): @require_user @validate() def list_files(query: ListFilesQuery): - stmt = select(File).where(File.user_id == g.user.id).limit(50).order_by(File.id.desc()) + stmt = ( + select(File).where(File.user_id == g.user.id).limit(50).order_by(File.id.desc()) + ) if query.before: stmt = stmt.where(File.id < query.before) if query.after: @@ -54,3 +58,20 @@ def list_files(query: ListFilesQuery): ).model_dump() for file in files ] + + +@bp.delete("/api/files/") +@maybe_token +@require_user +@validate() +def delete(id: int): + with Session(engine) as session: + file = session.scalar(select(File).where(File.id == id)) + if not file or g.user.id != file.user_id: + return Errors.FILE_NOT_FOUND + + session.delete(file) + session.commit() + delete_file(file.hash, file.content_type) + + return "", 204 diff --git a/foxfiles/errors.py b/foxfiles/errors.py index e63120d..b64a70e 100644 --- a/foxfiles/errors.py +++ b/foxfiles/errors.py @@ -12,3 +12,4 @@ class Errors: INVALID_TOKEN = ({"error": "Invalid token"}, 401) INVALID_CREDENTIALS = ({"error": "Invalid credentials"}, 400) MISSING_FILE = ({"error": "Missing file"}, 400) + FILE_NOT_FOUND = ({"error": "File not found"}, 404) diff --git a/foxfiles/files.py b/foxfiles/files.py index 0e2c8b6..672fc8b 100644 --- a/foxfiles/files.py +++ b/foxfiles/files.py @@ -44,6 +44,14 @@ def upload_file(file: FileStorage) -> tuple[str, str]: raise NotImplementedError() +def delete_file(hash: str, content_type: str): + match STORAGE_BACKEND: + case "local": + return _local_delete_file(hash, content_type) + case "s3": + raise NotImplementedError() + + def _local_get_file_by_hash(hash: str, content_type: str): p = _local_file_path(hash, content_type) with p.open("rb") as f: @@ -64,3 +72,11 @@ def _local_upload_file(file: FileStorage) -> tuple[str, str]: f.write(stream) return (hash, file_type) + + +def _local_delete_file(hash: str, content_type: str): + p = _local_file_path(hash, content_type) + try: + os.remove(p) + except FileNotFoundError: + pass