Spaces:
Sleeping
Sleeping
File size: 2,673 Bytes
f65e025 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | """Document endpoints: upload, list, detail, page images, re-process, delete."""
from __future__ import annotations
import uuid
from datetime import datetime, timezone
from fastapi import APIRouter, BackgroundTasks, File, HTTPException, UploadFile
from fastapi.responses import FileResponse
from app.core.config import settings
from app.core.logging import get_logger
from app.schemas.documents import DocStatus, DocumentDetail, DocumentMeta
from app.services import storage, vectorstore
from app.services.pipeline import process_document
log = get_logger(__name__)
router = APIRouter(prefix="/api/documents", tags=["documents"])
ALLOWED = {
".pdf", ".docx", ".doc", ".pptx", ".xlsx", ".html", ".md", ".txt",
".png", ".jpg", ".jpeg", ".tiff", ".bmp",
}
@router.post("", response_model=DocumentMeta)
async def upload(background: BackgroundTasks, file: UploadFile = File(...)):
ext = "." + (file.filename or "").rsplit(".", 1)[-1].lower() if "." in (file.filename or "") else ""
if ext not in ALLOWED:
raise HTTPException(400, f"Unsupported file type '{ext}'. Allowed: {sorted(ALLOWED)}")
doc_id = uuid.uuid4().hex[:12]
dest = settings.uploads_path / f"{doc_id}{ext}"
data = await file.read()
dest.write_bytes(data)
detail = DocumentDetail(
id=doc_id,
filename=file.filename or dest.name,
content_type=file.content_type or "application/octet-stream",
size_bytes=len(data),
status=DocStatus.uploaded,
created_at=datetime.now(timezone.utc).isoformat(),
)
storage.save(detail)
# kick off async processing
background.add_task(process_document, doc_id, dest)
log.info("Uploaded %s (%d bytes) -> %s", file.filename, len(data), doc_id)
return DocumentMeta.model_validate(detail.model_dump())
@router.get("", response_model=list[DocumentMeta])
async def list_documents():
return storage.list_all()
@router.get("/{doc_id}", response_model=DocumentDetail)
async def get_document(doc_id: str):
detail = storage.get(doc_id)
if not detail:
raise HTTPException(404, "Document not found")
return detail
@router.get("/{doc_id}/pages/{page}.png")
async def get_page_image(doc_id: str, page: int):
path = settings.renders_path / doc_id / f"page-{page}.png"
if not path.exists():
raise HTTPException(404, "Page image not found")
return FileResponse(path, media_type="image/png")
@router.delete("/{doc_id}")
async def delete_document(doc_id: str):
if not storage.get(doc_id):
raise HTTPException(404, "Document not found")
vectorstore.delete(doc_id)
storage.delete(doc_id)
return {"ok": True}
|