Paperjet/backend/app/api/v1/export.py

94 lines
3.1 KiB
Python

"""Flattened PDF export endpoint."""
import json
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import Response
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.auth.dependencies import get_current_user, verify_csrf
from app.config import settings
from app.db import get_db
from app.models.annotation_state import AnnotationState
from app.models.document import Document
from app.models.version import Version
from app.schemas.export import ExportRequest
from app.services.export.renderer import export_annotations
from app.services.storage import safe_filename
router = APIRouter(
prefix="/documents",
tags=["export"],
dependencies=[Depends(get_current_user)],
)
def _document_or_404(document_id: str, db: Session) -> Document:
document = db.scalar(
select(Document).where(Document.id == document_id, Document.deleted_at.is_(None))
)
if not document:
raise HTTPException(status_code=404, detail="Document not found")
return document
def _state_data(document_id: str, db: Session) -> list[dict[str, object]]:
state = db.scalar(select(AnnotationState).where(AnnotationState.document_id == document_id))
if not state:
return []
try:
data = json.loads(state.data)
except json.JSONDecodeError:
return []
return data if isinstance(data, list) else []
@router.post("/{document_id}/export", dependencies=[Depends(verify_csrf)])
def export_document(
document_id: str,
request: ExportRequest,
db: Session = Depends(get_db),
) -> Response:
"""Export the working layer or a selected version as a downloadable PDF."""
document = _document_or_404(document_id, db)
pdf_path = settings.PDF_STORAGE_PATH / f"{document.id}.pdf"
if not pdf_path.is_file():
raise HTTPException(status_code=404, detail="Original PDF is missing")
if not request.flatten:
content = pdf_path.read_bytes()
elif request.versionId:
version = db.scalar(
select(Version).where(
Version.id == request.versionId,
Version.document_id == document_id,
)
)
if not version:
raise HTTPException(status_code=404, detail="Version not found")
try:
version_data = json.loads(version.data)
except json.JSONDecodeError:
version_data = []
content = export_annotations(
pdf_path,
version_data if isinstance(version_data, list) else [],
settings.PDF_STORAGE_PATH / "assets" / document.id,
)
else:
content = export_annotations(
pdf_path,
_state_data(document_id, db),
settings.PDF_STORAGE_PATH / "assets" / document.id,
)
filename = safe_filename(document.original_filename)
if not filename.lower().endswith(".pdf"):
filename = f"{filename}.pdf"
filename = filename.replace('"', "'").replace("\r", "").replace("\n", "")
return Response(
content=content,
media_type="application/pdf",
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)