""" 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: return DocumentResponse( id=str(row['id']), path=row['path'], title=row['title'] or '', content=row['content'], frontmatter=dict(row['frontmatter'] or {}), 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'], )