Files
border-red-post-nalati/backend/app/api/routes/sources.py
T

102 lines
3.1 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query, Response
from sqlalchemy import or_, select
from sqlalchemy.orm import Session
from app.db.session import get_db
from app.models import Document, KnowledgeItem, Source
from app.schemas.catalog import AuditRead, Page, SourceCreate, SourceRead, SourceUpdate
from app.services.auth import require_admin
from app.services.catalog import (
ensure_version,
get_item,
history,
invalidate_knowledge,
page_result,
record_event,
update_values,
)
from app.services.indexing import catalog_write_guard
router = APIRouter(
prefix="/sources",
tags=["sources"],
dependencies=[Depends(require_admin), Depends(catalog_write_guard)],
)
@router.get("", response_model=Page[SourceRead])
def list_sources(
q: str = Query("", max_length=200),
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
db: Session = Depends(get_db),
):
statement = select(Source).where(Source.is_deleted.is_(False))
if q:
statement = statement.where(
or_(
Source.name.icontains(q, autoescape=True),
Source.notes.icontains(q, autoescape=True),
)
)
return page_result(db, statement.order_by(Source.id.desc()), page, page_size)
@router.post("", response_model=SourceRead, status_code=201)
def create_source(payload: SourceCreate, db: Session = Depends(get_db)):
item = Source(**payload.model_dump(mode="json"))
db.add(item)
record_event(db, item, "create")
db.commit()
return item
@router.get("/{item_id}", response_model=SourceRead)
def read_source(item_id: int, db: Session = Depends(get_db)):
return get_item(db, Source, item_id)
@router.patch("/{item_id}", response_model=SourceRead)
def update_source(item_id: int, payload: SourceUpdate, db: Session = Depends(get_db)):
item = get_item(db, Source, item_id, lock=True)
ensure_version(item, payload.expected_version)
update_values(item, payload)
record_event(db, item, "update")
invalidate_knowledge(
db, KnowledgeItem.source_id == item.id, source_url=item.source_url, update_url=True
)
db.commit()
return item
@router.delete("/{item_id}", status_code=204)
def delete_source(
item_id: int, expected_version: int = Query(..., ge=1), db: Session = Depends(get_db)
):
item = get_item(db, Source, item_id, lock=True)
ensure_version(item, expected_version)
for model in (Document, KnowledgeItem):
if (
db.scalar(
select(model.id)
.where(model.source_id == item_id, model.is_deleted.is_(False))
.limit(1)
)
is not None
):
raise HTTPException(409, "来源仍被文档或知识条目引用,不能删除")
item.is_deleted = True
record_event(db, item, "delete")
db.commit()
return Response(status_code=204)
@router.get("/{item_id}/history", response_model=Page[AuditRead])
def source_history(
item_id: int,
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
db: Session = Depends(get_db),
):
return history(db, Source, item_id, page, page_size)