from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from proxy_pool.common.dependencies import get_db, get_registry from proxy_pool.plugins.registry import PluginRegistry from proxy_pool.proxy.models import ProxySource from proxy_pool.proxy.schemas import ( ProxySourceCreate, ProxySourceResponse, ProxySourceUpdate, ) router = APIRouter(prefix="/sources", tags=["sources"]) @router.get("", response_model=list[ProxySourceResponse]) async def list_sources( is_active: bool | None = None, db: AsyncSession = Depends(get_db), ) -> list[ProxySourceResponse]: query = select(ProxySource) if is_active is not None: query = query.where(ProxySource.is_active == is_active) query = query.order_by(ProxySource.created_at.desc()) result = await db.execute(query) sources = result.scalars().all() return [ProxySourceResponse.model_validate(s) for s in sources] @router.post( "", response_model=ProxySourceResponse, status_code=status.HTTP_201_CREATED, ) async def create_source( body: ProxySourceCreate, db: AsyncSession = Depends(get_db), registry: PluginRegistry = Depends(get_registry), ) -> ProxySourceResponse: # Validate parser exists try: registry.get_parser(body.parser_name) except Exception: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail=f"No parser registered with name '{body.parser_name}'", ) from None source = ProxySource( url=body.url, parser_name=body.parser_name, cron_schedule=body.cron_schedule, default_protocol=body.default_protocol, ) db.add(source) await db.commit() await db.refresh(source) return ProxySourceResponse.model_validate(source) @router.get("/{source_id}", response_model=ProxySourceResponse) async def get_source( source_id: UUID, db: AsyncSession = Depends(get_db), ) -> ProxySourceResponse: source = await db.get(ProxySource, source_id) if source is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Source not found", ) return ProxySourceResponse.model_validate(source) @router.patch("/{source_id}", response_model=ProxySourceResponse) async def update_source( source_id: UUID, body: ProxySourceUpdate, db: AsyncSession = Depends(get_db), registry: PluginRegistry = Depends(get_registry), ) -> ProxySourceResponse: source = await db.get(ProxySource, source_id) if source is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Source not found", ) update_data = body.model_dump(exclude_unset=True) if "parser_name" in update_data: try: registry.get_parser(update_data["parser_name"]) except Exception: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail=f"No parser registered with name '{update_data['parser_name']}'", ) from None for field, value in update_data.items(): setattr(source, field, value) await db.commit() await db.refresh(source) return ProxySourceResponse.model_validate(source) @router.delete("/{source_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_source( source_id: UUID, db: AsyncSession = Depends(get_db), ) -> None: source = await db.get(ProxySource, source_id) if source is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Source not found", ) await db.delete(source) await db.commit()