- main.py: remove duplicate /health endpoint definition - text_extract.py: HTML-escape extracted plain text before indexing so raw markup in uploaded documents can't render as live HTML via search snippets (stored XSS fix) - document_service.py: base64-encode content when UTF-8 decoding fails instead of raising UnicodeDecodeError (500) on binary files; add upload validation for file extension (allowlist) and size (10MB max) - routers/documents.py: map new validation errors to HTTP 400 with a clear detail message instead of letting them 500 - add/extend tests covering escaping, binary get_document, and upload validation rejection + acceptance paths
59 lines
1.8 KiB
Python
59 lines
1.8 KiB
Python
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
|
|
from pydantic import BaseModel
|
|
|
|
from app.dependencies import get_document_service
|
|
from app.document_service import (
|
|
DocumentNotFoundError,
|
|
DocumentService,
|
|
FileTooLargeError,
|
|
UnsupportedFileTypeError,
|
|
)
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class SaveDocumentRequest(BaseModel):
|
|
content: str
|
|
contentType: str
|
|
|
|
|
|
@router.get("/api/documents")
|
|
def list_documents(path: str = "", service: DocumentService = Depends(get_document_service)):
|
|
return service.list_documents(path)
|
|
|
|
|
|
@router.get("/api/documents/{key:path}")
|
|
def get_document(key: str, service: DocumentService = Depends(get_document_service)):
|
|
try:
|
|
return service.get_document(key)
|
|
except DocumentNotFoundError:
|
|
raise HTTPException(status_code=404, detail="Document not found")
|
|
|
|
|
|
@router.put("/api/documents/{key:path}")
|
|
def save_document(
|
|
key: str, body: SaveDocumentRequest, service: DocumentService = Depends(get_document_service)
|
|
):
|
|
return service.save_document(key, body.content.encode("utf-8"), body.contentType)
|
|
|
|
|
|
@router.delete("/api/documents/{key:path}", status_code=204)
|
|
def delete_document(key: str, service: DocumentService = Depends(get_document_service)):
|
|
service.delete_document(key)
|
|
return None
|
|
|
|
|
|
@router.post("/api/upload", status_code=201)
|
|
async def upload_document(
|
|
file: UploadFile = File(...),
|
|
path: str = Form(...),
|
|
service: DocumentService = Depends(get_document_service),
|
|
):
|
|
data = await file.read()
|
|
key = f"{path.rstrip('/')}/{file.filename}" if path else file.filename
|
|
content_type = file.content_type or "application/octet-stream"
|
|
try:
|
|
return service.upload_document(key, data, content_type)
|
|
except (UnsupportedFileTypeError, FileTooLargeError) as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc))
|