From 7d01d2419688e799ca6628cb4c83f58b0aa35ae3 Mon Sep 17 00:00:00 2001 From: Michael Woods Date: Fri, 26 Dec 2025 00:12:02 -0500 Subject: [PATCH] get binary option from http api. --- packetserver/http/routers/objects.py | 68 +++++++++++++++++++++++++++- packetserver/server/objects.py | 4 ++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/packetserver/http/routers/objects.py b/packetserver/http/routers/objects.py index b29af46..5afb351 100644 --- a/packetserver/http/routers/objects.py +++ b/packetserver/http/routers/objects.py @@ -1,5 +1,5 @@ from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form -from fastapi.responses import PlainTextResponse, Response +from fastapi.responses import PlainTextResponse, Response, JSONResponse from typing import List, Optional from datetime import datetime from uuid import UUID @@ -422,4 +422,68 @@ async def get_object_text( logging.error(f"Text download failed for {username} on {uuid}: {e}\n{traceback.format_exc()}") raise HTTPException(status_code=500, detail="Failed to retrieve text object") - return PlainTextResponse(content=content, media_type="text/plain; charset=utf-8") \ No newline at end of file + return PlainTextResponse(content=content, media_type="text/plain; charset=utf-8") + +class ObjectBinaryResponse(BaseModel): + uuid: UUID + name: str + binary: bool + size: int + content_type: str + data_base64: str + private: bool + created_at: datetime + modified_at: datetime + +@router.get("/objects/{uuid}/binary", response_model=ObjectBinaryResponse) +async def get_object_binary( + uuid: UUID, + db: DbDependency, + current_user: HttpUser = Depends(get_current_http_user) +): + username = current_user.username + + try: + with db.transaction() as conn: + root = conn.root() + + obj = Object.get_object_by_uuid(uuid, root) + if not obj: + raise HTTPException(status_code=404, detail="Object not found") + + # Authorization check for private objects + if obj.private: + user = User.get_user_by_username(username, root) + if not user or user.uuid != obj.owner: + raise HTTPException(status_code=403, detail="Not authorized to access this private object") + + # Get content as bytes (works for both text and binary) + content_bytes = obj.data_bytes # uses the property that always returns bytes + + # Encode to base64 + data_base64 = base64.b64encode(content_bytes).decode('ascii') + + # Guess content_type + content_type, _ = mimetypes.guess_type(obj.name) + if content_type is None: + content_type = "application/octet-stream" if obj.binary else "text/plain" + + logging.info(f"User {username} downloaded binary/base64 object {uuid} ({obj.name})") + + except HTTPException: + raise + except Exception as e: + logging.error(f"Binary download failed for {username} on {uuid}: {e}\n{traceback.format_exc()}") + raise HTTPException(status_code=500, detail="Failed to retrieve object") + + return ObjectBinaryResponse( + uuid=obj.uuid, + name=obj.name, + binary=obj.binary, + size=obj.size, + content_type=content_type, + data_base64=data_base64, + private=obj.private, + created_at=obj.created_at, + modified_at=obj.modified_at + ) \ No newline at end of file diff --git a/packetserver/server/objects.py b/packetserver/server/objects.py index 5d0b16a..c4470aa 100644 --- a/packetserver/server/objects.py +++ b/packetserver/server/objects.py @@ -78,6 +78,10 @@ class Object(persistent.Persistent): self._binary = False self.touch() + @property + def data_bytes(self): + return self._data + @property def owner(self) -> Optional[UUID]: return self._owner