diff --git a/packetserver/http/routers/objects.py b/packetserver/http/routers/objects.py index 8441cd3..44b2ca7 100644 --- a/packetserver/http/routers/objects.py +++ b/packetserver/http/routers/objects.py @@ -6,13 +6,16 @@ from uuid import UUID import mimetypes import logging from traceback import format_exc +import base64 + +from pydantic import BaseModel from packetserver.http.dependencies import get_current_http_user from packetserver.http.auth import HttpUser from packetserver.http.database import DbDependency from packetserver.server.objects import Object from packetserver.server.users import User -from pydantic import BaseModel + router = APIRouter(prefix="/api/v1", tags=["objects"]) @@ -185,4 +188,69 @@ async def create_text_object( private=new_object.private, created_at=new_object.created_at, modified_at=new_object.modified_at + ) + +class BinaryObjectCreate(BaseModel): + data_base64: str + name: Optional[str] = None + private: bool = True + +@router.post("/objects/binary", response_model=ObjectSummary) +async def create_binary_object( + payload: BinaryObjectCreate, + db: DbDependency, + current_user: HttpUser = Depends(get_current_http_user) +): + username = current_user.username + + # Decode base64 + try: + content = base64.b64decode(payload.data_base64, validate=True) + except Exception as e: + raise HTTPException(status_code=400, detail="Invalid base64 encoding") + + if not content: + raise HTTPException(status_code=400, detail="Binary content cannot be empty") + + obj_name = (payload.name or "binary_object.bin").strip() + if len(obj_name) > 300: + raise HTTPException(status_code=400, detail="Object name too long (max 300 chars)") + if not obj_name: + raise HTTPException(status_code=400, detail="Invalid object name") + + try: + with db.transaction() as conn: + root = conn.root() + user = User.get_user_by_username(username, root) + if not user: + raise HTTPException(status_code=404, detail="User not found") + + # Pass bytes → forces binary=True + new_object = Object(name=obj_name, data=content) + new_object.private = payload.private + + obj_uuid = new_object.write_new(db, username=username) + + logging.info(f"User {username} created binary object {obj_uuid} ({obj_name}, {len(content)} bytes via base64)") + + except HTTPException: + raise + except Exception as e: + logging.error(f"Binary object creation failed for {username}: {e}\n{format_exc()}") + raise HTTPException(status_code=500, detail="Failed to create binary object") + + # Build summary + content_type, _ = mimetypes.guess_type(new_object.name) + if content_type is None: + content_type = "application/octet-stream" # always safe for binary + + return ObjectSummary( + uuid=obj_uuid, + name=new_object.name, + binary=new_object.binary, # should be True + size=new_object.size, + content_type=content_type, + private=new_object.private, + created_at=new_object.created_at, + modified_at=new_object.modified_at ) \ No newline at end of file