Rescue Paperjet v1 implementation

This commit is contained in:
Elijah 2026-08-14 21:05:22 -07:00
parent 7ff5c8130d
commit db9e2ca51b
83 changed files with 6186 additions and 3894 deletions

View file

@ -0,0 +1,94 @@
"""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}"'},
)