feat: update document editor routes to include search space context

- Modified API endpoints to include search space ID in the URL for fetching and saving documents.
- Added permission checks for reading and updating documents based on search space.
This commit is contained in:
DESKTOP-RTLN3BA\$punk 2025-11-30 14:27:27 -08:00
parent eefecfa4b9
commit d0c7be7eca
2 changed files with 40 additions and 12 deletions

View file

@ -8,15 +8,20 @@ from typing import Any
from fastapi import APIRouter, Depends, HTTPException from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from app.db import Document, SearchSpace, User, get_async_session from app.db import Document, Permission, User, get_async_session
from app.users import current_active_user from app.users import current_active_user
from app.utils.rbac import check_permission
router = APIRouter() router = APIRouter()
@router.get("/documents/{document_id}/editor-content") @router.get(
"/search-spaces/{search_space_id}/documents/{document_id}/editor-content"
)
async def get_editor_content( async def get_editor_content(
search_space_id: int,
document_id: int, document_id: int,
session: AsyncSession = Depends(get_async_session), session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user), user: User = Depends(current_active_user),
@ -26,14 +31,25 @@ async def get_editor_content(
Returns BlockNote JSON document. If blocknote_document is NULL, Returns BlockNote JSON document. If blocknote_document is NULL,
attempts to generate it from chunks (lazy migration). attempts to generate it from chunks (lazy migration).
Requires DOCUMENTS_READ permission.
""" """
from sqlalchemy.orm import selectinload # Check RBAC permission
await check_permission(
session,
user,
search_space_id,
Permission.DOCUMENTS_READ.value,
"You don't have permission to read documents in this search space",
)
result = await session.execute( result = await session.execute(
select(Document) select(Document)
.options(selectinload(Document.chunks)) .options(selectinload(Document.chunks))
.join(SearchSpace) .filter(
.filter(Document.id == document_id, SearchSpace.user_id == user.id) Document.id == document_id,
Document.search_space_id == search_space_id,
)
) )
document = result.scalars().first() document = result.scalars().first()
@ -94,8 +110,9 @@ async def get_editor_content(
} }
@router.post("/documents/{document_id}/save") @router.post("/search-spaces/{search_space_id}/documents/{document_id}/save")
async def save_document( async def save_document(
search_space_id: int,
document_id: int, document_id: int,
data: dict[str, Any], data: dict[str, Any],
session: AsyncSession = Depends(get_async_session), session: AsyncSession = Depends(get_async_session),
@ -104,14 +121,25 @@ async def save_document(
""" """
Save BlockNote document and trigger reindexing. Save BlockNote document and trigger reindexing.
Called when user clicks 'Save & Exit'. Called when user clicks 'Save & Exit'.
Requires DOCUMENTS_UPDATE permission.
""" """
from app.tasks.celery_tasks.document_reindex_tasks import reindex_document_task from app.tasks.celery_tasks.document_reindex_tasks import reindex_document_task
# Verify ownership # Check RBAC permission
await check_permission(
session,
user,
search_space_id,
Permission.DOCUMENTS_UPDATE.value,
"You don't have permission to update documents in this search space",
)
result = await session.execute( result = await session.execute(
select(Document) select(Document).filter(
.join(SearchSpace) Document.id == document_id,
.filter(Document.id == document_id, SearchSpace.user_id == user.id) Document.search_space_id == search_space_id,
)
) )
document = result.scalars().first() document = result.scalars().first()

View file

@ -45,7 +45,7 @@ export default function EditorPage() {
try { try {
const response = await fetch( const response = await fetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/documents/${documentId}/editor-content`, `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/search-spaces/${params.search_space_id}/documents/${documentId}/editor-content`,
{ {
headers: { headers: {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
@ -114,7 +114,7 @@ export default function EditorPage() {
try { try {
// Save blocknote_document and trigger reindexing in background // Save blocknote_document and trigger reindexing in background
const response = await fetch( const response = await fetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/documents/${documentId}/save`, `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/search-spaces/${params.search_space_id}/documents/${documentId}/save`,
{ {
method: "POST", method: "POST",
headers: { headers: {