diff --git a/src/proxy_pool/app.py b/src/proxy_pool/app.py index 0c4904e..42eb622 100644 --- a/src/proxy_pool/app.py +++ b/src/proxy_pool/app.py @@ -9,7 +9,8 @@ from proxy_pool.config import get_settings from proxy_pool.db.session import create_session_factory from proxy_pool.plugins.discovery import discover_plugins from proxy_pool.plugins.registry import PluginRegistry -from proxy_pool.proxy.router import router as proxy_router +from proxy_pool.proxy.router import proxy_router +from proxy_pool.proxy.router import router as source_router logger = logging.getLogger(__name__) @@ -60,6 +61,7 @@ def create_app() -> FastAPI: ) app.include_router(health_router) + app.include_router(source_router) app.include_router(proxy_router) return app diff --git a/src/proxy_pool/proxy/router.py b/src/proxy_pool/proxy/router.py index 7cf6915..9b88e65 100644 --- a/src/proxy_pool/proxy/router.py +++ b/src/proxy_pool/proxy/router.py @@ -6,14 +6,19 @@ 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.models import Proxy, ProxySource from proxy_pool.proxy.schemas import ( + ProxyListParams, + ProxyListResponse, + ProxyResponse, ProxySourceCreate, ProxySourceResponse, ProxySourceUpdate, ) +from proxy_pool.proxy.service import query_proxies router = APIRouter(prefix="/sources", tags=["sources"]) +proxy_router = APIRouter(prefix="/proxies", tags=["proxies"]) @router.get("", response_model=list[ProxySourceResponse]) @@ -124,3 +129,47 @@ async def delete_source( ) await db.delete(source) await db.commit() + + +@proxy_router.get("", response_model=ProxyListResponse) +async def list_proxies( + params: ProxyListParams = Depends(), + db: AsyncSession = Depends(get_db), +) -> ProxyListResponse: + proxies, total = await query_proxies( + db, + status=params.status, + protocol=params.protocol, + anonymity=params.anonymity, + country=params.country, + min_score=params.min_score, + max_latency_ms=params.max_latency_ms, + min_uptime_pct=params.min_uptime_pct, + verified_within_minutes=params.verified_within_minutes, + sort_by=params.sort_by, + sort_order=params.sort_order, + limit=params.limit, + offset=params.offset, + ) + + return ProxyListResponse( + items=[ProxyResponse.model_validate(p) for p in proxies], + total_count=total, + limit=params.limit, + offset=params.offset, + ) + + +@proxy_router.get("/{proxy_id}", response_model=ProxyResponse) +async def get_proxy( + proxy_id: UUID, + db: AsyncSession = Depends(get_db), +) -> ProxyResponse: + proxy = await db.get(Proxy, proxy_id) + if proxy is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Proxy not found", + ) from None + + return ProxyResponse.model_validate(proxy) diff --git a/src/proxy_pool/proxy/schemas.py b/src/proxy_pool/proxy/schemas.py index 4d2743e..81de9b3 100644 --- a/src/proxy_pool/proxy/schemas.py +++ b/src/proxy_pool/proxy/schemas.py @@ -51,3 +51,25 @@ class ProxyResponse(BaseModel): last_checked_at: datetime | None first_seen_at: datetime created_at: datetime + + +class ProxyListParams(BaseModel): + status: ProxyStatus | None = None + protocol: ProxyProtocol | None = None + anonymity: AnonymityLevel | None = None + country: str | None = Field(default=None, min_length=2, max_length=2) + min_score: float | None = Field(default=None, ge=0.0, le=1.0) + max_latency_ms: float | None = Field(default=None, gt=0) + min_uptime_pct: float | None = Field(default=None, ge=0.0, le=100.0) + verified_within_minutes: int | None = Field(default=None, gt=0) + sort_by: str = Field(default="score") + sort_order: str = Field(default="desc", pattern="^(asc|desc)$") + limit: int = Field(default=50, ge=1, le=200) + offset: int = Field(default=0, ge=0) + + +class ProxyListResponse(BaseModel): + items: list[ProxyResponse] + total_count: int + limit: int + offset: int