Added some logging and object upload code.
This commit is contained in:
13
packetserver/http/logging.py
Normal file
13
packetserver/http/logging.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from .config import Settings
|
||||
import logging
|
||||
|
||||
|
||||
def init_logging():
|
||||
settings = Settings()
|
||||
|
||||
desired_level = settings.log_level.upper().strip()
|
||||
|
||||
if desired_level not in logging.getLevelNamesMapping():
|
||||
raise ValueError(f"Invalid log level '{desired_level}'")
|
||||
|
||||
logging.basicConfig(level=logging.getLevelName(desired_level))
|
||||
@@ -1,13 +1,17 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from typing import List
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form
|
||||
from fastapi.responses import JSONResponse
|
||||
from typing import List, Optional
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
import mimetypes
|
||||
import logging
|
||||
from traceback import format_exc
|
||||
|
||||
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"])
|
||||
@@ -50,3 +54,78 @@ async def list_my_objects(db: DbDependency, current_user: HttpUser = Depends(get
|
||||
))
|
||||
|
||||
return user_objects
|
||||
|
||||
@router.post("/objects", response_model=ObjectSummary)
|
||||
async def upload_object(
|
||||
db: DbDependency,
|
||||
file: UploadFile = File(...),
|
||||
name: Optional[str] = Form(None),
|
||||
private: bool = Form(True),
|
||||
force_text: bool = Form(False), # NEW: force treat as UTF-8 text
|
||||
current_user: HttpUser = Depends(get_current_http_user)
|
||||
):
|
||||
username = current_user.username
|
||||
|
||||
content = await file.read()
|
||||
if not content:
|
||||
raise HTTPException(status_code=400, detail="Empty file upload")
|
||||
|
||||
obj_name = (name or file.filename or "unnamed_object").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")
|
||||
|
||||
# Handle force_text logic
|
||||
if force_text:
|
||||
try:
|
||||
text_content = content.decode('utf-8', errors='strict')
|
||||
object_data = text_content # str → will set binary=False
|
||||
except UnicodeDecodeError:
|
||||
raise HTTPException(status_code=400, detail="Content is not valid UTF-8 and cannot be forced as text")
|
||||
else:
|
||||
object_data = content # bytes → will set binary=True
|
||||
|
||||
# Create and persist the object
|
||||
new_object = Object(name=obj_name, data=object_data)
|
||||
new_object.private = private
|
||||
|
||||
obj_uuid = new_object.write_new(db) # assuming write_new takes db and handles root.objects storage
|
||||
|
||||
new_object.chown(username, db)
|
||||
|
||||
if force_text:
|
||||
obj_type = 'string'
|
||||
else:
|
||||
obj_type = 'binary'
|
||||
|
||||
logging.info(f"User {username} uploaded {obj_type} object {obj_uuid} ({obj_name}, {len(content)} bytes, force_text={force_text})")
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logging.error(f"Object upload failed for {username}: {e}\n{format_exc()}")
|
||||
raise HTTPException(status_code=500, detail="Failed to store object")
|
||||
|
||||
# Build summary (matching your existing list endpoint)
|
||||
content_type, _ = mimetypes.guess_type(new_object.name)
|
||||
if content_type is None:
|
||||
content_type = "application/octet-stream" if new_object.binary else "text/plain"
|
||||
|
||||
return ObjectSummary(
|
||||
uuid=obj_uuid,
|
||||
name=new_object.name,
|
||||
binary=new_object.binary,
|
||||
size=new_object.size,
|
||||
content_type=content_type,
|
||||
private=new_object.private,
|
||||
created_at=new_object.created_at,
|
||||
modified_at=new_object.modified_at
|
||||
)
|
||||
@@ -6,6 +6,9 @@ from pathlib import Path
|
||||
|
||||
from .database import init_db
|
||||
from .routers import public, profile, messages, send
|
||||
from .logging import init_logging
|
||||
|
||||
init_logging()
|
||||
|
||||
BASE_DIR = Path(__file__).parent.resolve()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user