feat: add proxy source crud and wire into app

This commit is contained in:
agatha 2026-03-14 16:02:47 -04:00
parent d249020773
commit ca58dd94db
2 changed files with 130 additions and 2 deletions

View File

@ -4,10 +4,12 @@ from contextlib import asynccontextmanager
from fastapi import FastAPI from fastapi import FastAPI
from redis.asyncio import from_url as redis_from_url from redis.asyncio import from_url as redis_from_url
from proxy_pool.common.router import router as health_router
from proxy_pool.config import get_settings from proxy_pool.config import get_settings
from proxy_pool.db.session import create_session_factory from proxy_pool.db.session import create_session_factory
from proxy_pool.plugins.discovery import discover_plugins from proxy_pool.plugins.discovery import discover_plugins
from proxy_pool.plugins.registry import PluginRegistry from proxy_pool.plugins.registry import PluginRegistry
from proxy_pool.proxy.router import router as proxy_router
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -57,7 +59,7 @@ def create_app() -> FastAPI:
lifespan=lifespan, lifespan=lifespan,
) )
# Register routers here as we build them app.include_router(health_router)
# app.include_router(...) app.include_router(proxy_router)
return app return app

View File

@ -0,0 +1,126 @@
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_ENTITY,
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_ENTITY,
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()