DocHub/backend/app/routers/documents.py
Tianyang 13789cc0b4 fix: remove duplicate /health route, escape extracted text, validate uploads, handle binary decode
- 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
2026-08-01 08:59:34 +08:00

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))