FastAPI + Celery + Next.js + Postgres/Redis app with company monitoring, source collection, LLM-based change analysis, enrichment, and account security (Turnstile, escalating lockout, email verification).
84 lines
2.8 KiB
Python
84 lines
2.8 KiB
Python
"""Source routes. Mixes `/companies/{company_id}/sources` and
|
|
`/sources/{source_id}` paths per the spec - kept in one router since both
|
|
share the same schemas/service."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import uuid
|
|
|
|
from fastapi import APIRouter, Depends, Request, status
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.auth.dependencies import get_current_user
|
|
from app.core.config import Settings, get_settings
|
|
from app.core.rate_limit import limiter
|
|
from app.db.session import get_db
|
|
from app.models.user import User
|
|
from app.schemas.source import SourceCreate, SourceResponse, SourceTestResult, SourceUpdate
|
|
from app.services import source_service
|
|
|
|
router = APIRouter(tags=["sources"])
|
|
|
|
|
|
@router.get("/companies/{company_id}/sources", response_model=list[SourceResponse])
|
|
async def list_sources(
|
|
company_id: uuid.UUID,
|
|
user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
) -> list[SourceResponse]:
|
|
sources = await source_service.list_sources(db, user.id, company_id)
|
|
return [SourceResponse.model_validate(s) for s in sources]
|
|
|
|
|
|
@router.post(
|
|
"/companies/{company_id}/sources",
|
|
response_model=SourceResponse,
|
|
status_code=status.HTTP_201_CREATED,
|
|
)
|
|
@limiter.limit("30/minute")
|
|
async def create_source(
|
|
request: Request,
|
|
company_id: uuid.UUID,
|
|
payload: SourceCreate,
|
|
user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
) -> SourceResponse:
|
|
source = await source_service.create_source(db, user.id, company_id, payload)
|
|
return SourceResponse.model_validate(source)
|
|
|
|
|
|
@router.patch("/sources/{source_id}", response_model=SourceResponse)
|
|
async def update_source(
|
|
source_id: uuid.UUID,
|
|
payload: SourceUpdate,
|
|
user: User = Depends(get_current_user),
|
|
settings: Settings = Depends(get_settings),
|
|
db: AsyncSession = Depends(get_db),
|
|
) -> SourceResponse:
|
|
source = await source_service.update_source(db, settings, user.id, source_id, payload)
|
|
return SourceResponse.model_validate(source)
|
|
|
|
|
|
@router.delete("/sources/{source_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def delete_source(
|
|
source_id: uuid.UUID,
|
|
user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
) -> None:
|
|
await source_service.delete_source(db, user.id, source_id)
|
|
|
|
|
|
@router.post("/sources/{source_id}/test", response_model=SourceTestResult)
|
|
@limiter.limit("20/minute")
|
|
async def test_source(
|
|
request: Request,
|
|
source_id: uuid.UUID,
|
|
user: User = Depends(get_current_user),
|
|
settings: Settings = Depends(get_settings),
|
|
db: AsyncSession = Depends(get_db),
|
|
) -> SourceTestResult:
|
|
result = await source_service.test_source(db, settings, user.id, source_id)
|
|
return SourceTestResult(
|
|
status=result.status, documents_found=len(result.documents), error=result.error
|
|
)
|