Files
cvfs/apps/backend/fastapi/app/api/routes/documents.py
Claude e6c29f3bd4 Redesign webapp with minimal style and full backend integration
- New monochrome design system in globals.css (no shadows, tight spacing, system-font)
- Root layout stripped to html/body; home page owns its Header/Footer
- Dashboard fully wired to FastAPI backend: upload, branch, submission, publish, download
- CVTree rebuilt as compact file-tree component driven by real version data
- DiffViewer rebuilt as minimal git-diff display using real patch data
- API client (libs/api.ts) extended with all CRUD operations
- Header redesigned to match minimal style
- Backend: added GET /documents/{id}/versions/{id}/download endpoint for DOCX export

https://claude.ai/code/session_01Xmxm2QLgFBgRJyYD6VukR6
2026-04-02 18:12:25 +00:00

77 lines
2.8 KiB
Python

from __future__ import annotations
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
from fastapi.responses import Response
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_current_user, get_db
from app.schemas import DocumentListResponse, DocumentResponse
from app.services.documents import create_document, get_document, list_documents
from app.services.storage import storage_client
from dlib.auth import AuthenticatedUser
router = APIRouter(prefix="/documents", tags=["documents"])
@router.get("/", response_model=DocumentListResponse)
async def list_user_documents(
session: AsyncSession = Depends(get_db),
user: AuthenticatedUser = Depends(get_current_user),
):
documents = await list_documents(session, owner_id=user.sub)
payload = [DocumentResponse.model_validate(doc) for doc in documents]
return DocumentListResponse(items=payload)
@router.get("/{document_id}", response_model=DocumentResponse)
async def get_user_document(
document_id: str,
session: AsyncSession = Depends(get_db),
user: AuthenticatedUser = Depends(get_current_user),
):
document = await get_document(session, owner_id=user.sub, document_id=document_id)
if not document:
raise HTTPException(status_code=404, detail="Document not found")
return DocumentResponse.model_validate(document)
@router.get("/{document_id}/versions/{version_id}/download")
async def download_version_docx(
document_id: str,
version_id: str,
session: AsyncSession = Depends(get_db),
user: AuthenticatedUser = Depends(get_current_user),
):
document = await get_document(session, owner_id=user.sub, document_id=document_id)
if not document:
raise HTTPException(status_code=404, detail="Document not found")
version = next((v for v in document.versions if v.id == version_id), None)
if not version or not version.artifact_docx_key:
raise HTTPException(status_code=404, detail="Version artifact not found")
data = storage_client.download_bytes(key=version.artifact_docx_key)
slug = f"{document.title.replace(' ', '-')}-{version.branch_name}.docx"
return Response(
content=data,
media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
headers={"Content-Disposition": f'attachment; filename="{slug}"'},
)
@router.post("/", response_model=DocumentResponse)
async def upload_document(
title: str = Form(...),
description: str | None = Form(default=None),
file: UploadFile = File(...),
session: AsyncSession = Depends(get_db),
user: AuthenticatedUser = Depends(get_current_user),
):
document = await create_document(
session,
owner_id=user.sub,
title=title,
description=description,
upload=file,
)
return DocumentResponse.model_validate(document)