from typing import Annotated, Any
from fastapi import APIRouter, Depends, HTTPException, Query
from app.core.context import get_user_id
from app.core.deps import get_current_user
from app.schemas.knowledge import (
KnowledgeEntryCreate,
KnowledgeEntryItem,
KnowledgeEntryUpdate,
KnowledgeListResponse,
)
from app.services import knowledge_store
router = APIRouter(prefix="/api/knowledge", tags=["knowledge"], dependencies=[Depends(get_current_user)])
def _to_item(row: dict[str, Any], *, with_content: bool = False) -> KnowledgeEntryItem:
if with_content and "content" not in row:
full = knowledge_store.get_entry(row["id"], include_content=True)
if full:
row = {**row, "content": full.get("content", "")}
return KnowledgeEntryItem(
id=str(row["id"]),
title=str(row["title"]),
category=str(row.get("category") or "general"),
tags=list(row.get("tags") or []),
summary=str(row.get("summary") or ""),
content=str(row.get("content") or ""),
created_by=str(row.get("created_by") or ""),
created_at=str(row.get("created_at") or ""),
updated_at=str(row.get("updated_at") or ""),
)
@router.get("", response_model=KnowledgeListResponse)
async def list_knowledge(
category: str | None = None,
keyword: str | None = Query(default=None),
limit: int = Query(default=50, ge=1, le=100),
):
rows = knowledge_store.list_entries(category=category, keyword=keyword, limit=limit)
return KnowledgeListResponse(items=[_to_item(r) for r in rows])
@router.get("/search", response_model=KnowledgeListResponse)
async def search_knowledge(
q: str = Query(..., min_length=1),
limit: int = Query(default=20, ge=1, le=50),
):
rows = knowledge_store.search_entries(q, limit=limit)
return KnowledgeListResponse(items=[_to_item(r) for r in rows])
@router.get("/{entry_id}", response_model=KnowledgeEntryItem)
async def get_knowledge(entry_id: str):
row = knowledge_store.get_entry(entry_id, include_content=True)
if not row:
raise HTTPException(status_code=404, detail="未找到该条目")
return _to_item(row, with_content=True)
@router.post("", response_model=KnowledgeEntryItem)
async def create_knowledge(
payload: KnowledgeEntryCreate,
user: Annotated[dict[str, Any], Depends(get_current_user)],
):
try:
row = knowledge_store.create_entry(
payload.title,
payload.content,
category=payload.category,
tags=payload.tags,
summary=payload.summary,
created_by=str(user.get("username") or get_user_id() or ""),
)
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
full = knowledge_store.get_entry(row["id"], include_content=True)
return _to_item(full or row, with_content=True)
@router.patch("/{entry_id}", response_model=KnowledgeEntryItem)
async def update_knowledge(entry_id: str, payload: KnowledgeEntryUpdate):
patch = payload.model_dump(exclude_unset=True)
if not patch:
row = knowledge_store.get_entry(entry_id, include_content=True)
if not row:
raise HTTPException(status_code=404, detail="未找到该条目")
return _to_item(row, with_content=True)
try:
row = knowledge_store.update_entry(entry_id, patch)
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
full = knowledge_store.get_entry(row["id"], include_content=True)
return _to_item(full or row, with_content=True)
@router.delete("/{entry_id}")
async def delete_knowledge(entry_id: str):
try:
knowledge_store.delete_entry(entry_id)
except ValueError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc
return {"ok": True}