SurfSense/surfsense_backend/app/routes/export_routes.py

64 lines
1.9 KiB
Python
Raw Normal View History

2026-04-09 12:10:37 +02:00
"""Routes for exporting knowledge base content as ZIP."""
import logging
import os
2026-04-09 12:10:37 +02:00
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import StreamingResponse
2026-04-09 12:10:37 +02:00
from sqlalchemy.ext.asyncio import AsyncSession
from app.db import Permission, User, get_async_session
from app.services.export_service import build_export_zip
2026-04-09 12:10:37 +02:00
from app.users import current_active_user
from app.utils.rbac import check_permission
logger = logging.getLogger(__name__)
router = APIRouter()
@router.get("/search-spaces/{search_space_id}/export")
async def export_knowledge_base(
search_space_id: int,
2026-04-14 01:43:30 -07:00
folder_id: int | None = Query(
None, description="Export only this folder's subtree"
),
2026-04-09 12:10:37 +02:00
session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user),
):
"""Export documents as a ZIP of markdown files preserving folder structure."""
2026-04-09 12:10:37 +02:00
await check_permission(
session,
user,
search_space_id,
Permission.DOCUMENTS_READ.value,
"You don't have permission to export documents in this search space",
)
try:
result = await build_export_zip(session, search_space_id, folder_id)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e)) from None
def stream_and_cleanup():
try:
with open(result.zip_path, "rb") as f:
while chunk := f.read(8192):
yield chunk
finally:
os.unlink(result.zip_path)
headers = {
"Content-Disposition": f'attachment; filename="{result.export_name}.zip"',
"Content-Length": str(result.zip_size),
}
if result.skipped_docs:
headers["X-Skipped-Documents"] = str(len(result.skipped_docs))
return StreamingResponse(
stream_and_cleanup(),
media_type="application/zip",
headers=headers,
)