You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
78 lines
2.4 KiB
78 lines
2.4 KiB
"""
|
|
routers/documents.py — Document CRUD and graph endpoints.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Optional
|
|
from fastapi import APIRouter, HTTPException, Depends
|
|
import asyncpg
|
|
|
|
from core.database import get_pool
|
|
from core.settings import Settings
|
|
from models.responses import DocumentResponse, GraphResponse, GraphNode, GraphEdge, RelatedDocument, TagCount
|
|
from services.retriever import get_related
|
|
|
|
router = APIRouter(prefix='/document', tags=['documents'])
|
|
|
|
|
|
def _get_settings() -> Settings:
|
|
from main import app_settings
|
|
return app_settings
|
|
|
|
|
|
@router.get('/{document_id}', response_model=DocumentResponse)
|
|
async def get_document(document_id: str):
|
|
pool = await get_pool()
|
|
async with pool.acquire() as conn:
|
|
row = await conn.fetchrow(
|
|
'SELECT * FROM documents WHERE id = $1::uuid', document_id
|
|
)
|
|
if not row:
|
|
raise HTTPException(status_code=404, detail='Document not found')
|
|
return _row_to_doc(row)
|
|
|
|
|
|
@router.get('/path/{path:path}', response_model=DocumentResponse)
|
|
async def get_document_by_path(path: str):
|
|
pool = await get_pool()
|
|
async with pool.acquire() as conn:
|
|
row = await conn.fetchrow('SELECT * FROM documents WHERE path = $1', path)
|
|
if not row:
|
|
raise HTTPException(status_code=404, detail='Document not found')
|
|
return _row_to_doc(row)
|
|
|
|
|
|
@router.get('/{document_id}/related', response_model=list[RelatedDocument])
|
|
async def related_documents(document_id: str, limit: int = 5):
|
|
pool = await get_pool()
|
|
async with pool.acquire() as conn:
|
|
related = await get_related(conn, document_id, limit=limit)
|
|
return [RelatedDocument(**r) for r in related]
|
|
|
|
|
|
def _row_to_doc(row: asyncpg.Record) -> DocumentResponse:
|
|
# Handle frontmatter - asyncpg returns JSONB as dict directly
|
|
fm = row['frontmatter']
|
|
if fm is None:
|
|
frontmatter = {}
|
|
elif isinstance(fm, dict):
|
|
frontmatter = fm
|
|
else:
|
|
# Fallback for unexpected types
|
|
frontmatter = {}
|
|
|
|
return DocumentResponse(
|
|
id=str(row['id']),
|
|
path=row['path'],
|
|
title=row['title'] or '',
|
|
content=row['content'],
|
|
frontmatter=frontmatter,
|
|
tags=list(row['tags'] or []),
|
|
aliases=list(row['aliases'] or []),
|
|
word_count=row['word_count'],
|
|
created_at=row['created_at'],
|
|
updated_at=row['updated_at'],
|
|
indexed_at=row['indexed_at'],
|
|
)
|