"""Pydantic 模型 - 用于 API 请求/响应校验""" from pydantic import BaseModel, Field, field_validator from typing import Optional, List class ProxyCreate(BaseModel): ip: str port: int = Field(ge=1, le=65535) protocol: str = "http" score: int = Field(default=10, ge=0, le=100) @field_validator("protocol") @classmethod def validate_protocol(cls, v: str): v = v.lower().strip() if v not in ("http", "https", "socks4", "socks5"): raise ValueError("protocol must be http, https, socks4 or socks5") return v class ProxyResponse(BaseModel): ip: str port: int protocol: str score: int response_time_ms: Optional[float] = None last_check: Optional[str] = None class PluginResponse(BaseModel): id: str name: str display_name: str description: str enabled: bool last_run: Optional[str] = None success_count: int = 0 failure_count: int = 0 class SettingsSchema(BaseModel): crawl_timeout: int = Field(default=30, ge=5, le=120) validation_timeout: int = Field(default=10, ge=3, le=60) max_retries: int = Field(default=3, ge=0, le=10) default_concurrency: int = Field(default=50, ge=10, le=200) min_proxy_score: int = Field(default=0, ge=0, le=100) proxy_expiry_days: int = Field(default=7, ge=1, le=30) auto_validate: bool = True validate_interval_minutes: int = Field(default=30, ge=5, le=1440) class CrawlResult(BaseModel): plugin_id: str proxy_count: int valid_count: int invalid_count: int = 0 class ProxyListRequest(BaseModel): page: int = Field(default=1, ge=1) page_size: int = Field(default=20, ge=1, le=100) protocol: Optional[str] = None min_score: int = Field(default=0, ge=0) max_score: Optional[int] = Field(default=None, ge=0) sort_by: str = "last_check" sort_order: str = "DESC" @field_validator("protocol") @classmethod def validate_protocol(cls, v): if v is not None and v.lower() not in ("http", "https", "socks4", "socks5"): raise ValueError("协议类型必须是 http, https, socks4 或 socks5") return v.lower() if v else v @field_validator("sort_by") @classmethod def validate_sort_by(cls, v): if v not in ("ip", "port", "protocol", "score", "last_check"): raise ValueError("排序字段必须是 ip, port, protocol, score 或 last_check") return v @field_validator("sort_order") @classmethod def validate_sort_order(cls, v): if v.upper() not in ("ASC", "DESC"): raise ValueError("排序方式必须是 ASC 或 DESC") return v.upper() class ProxyDeleteItem(BaseModel): ip: str port: int = Field(ge=1, le=65535) class BatchDeleteRequest(BaseModel): proxies: List[ProxyDeleteItem] = Field(max_length=1000) class PluginToggleRequest(BaseModel): enabled: bool class ExportRequest(BaseModel): format: str = Field(pattern=r"^(csv|txt|json)$") protocol: Optional[str] = None limit: int = Field(default=10000, ge=1, le=100000)