document_agent / backend /app /api /documents.py
Jai-rathore29's picture
Deploy: DocAgent backend (deterministic date-anomaly fix)
f65e025
Raw
History Blame Contribute Delete
2.67 kB
"""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}