- Add Free_Proxy_Website-style fpw_* plugins and register them - Per-plugin crawl timeout (crawl_timeout_seconds=120); remove global crawl_timeout setting - Validator: fix connect vs total timeout on save; SOCKS session LRU cache; drop redundant semaphore - Validation handler uses single DB connection; batch upsert after crawl; WorkerPool put_nowait - Remove unused max_retries from settings API/UI; settings maintenance SQL + init_db cleanup of deprecated keys - WebSocket dashboard stats; ProxyList pool_filter and API alignment - POST /api/proxies/delete-one for IPv6-safe deletes; task poll stops on 404 - pytest uses PROXYPOOL_DB_PATH=db/proxies.test.sqlite so tests do not wipe production DB - .gitignore: explicit proxies.test.sqlite patterns; fix plugin_service ValidationException import Made-with: Cursor
73 lines
3.1 KiB
Python
73 lines
3.1 KiB
Python
"""设置相关路由"""
|
||
import asyncio
|
||
|
||
from fastapi import APIRouter, Request, Depends
|
||
from app.core.db import get_db
|
||
from app.repositories.settings_repo import SettingsRepository
|
||
from app.models.schemas import SettingsSchema
|
||
from app.api.common import success_response
|
||
from app.api.deps import get_settings_repo
|
||
from app.core.config import settings as app_settings
|
||
from app.core.exceptions import ProxyPoolException
|
||
from app.core.log import logger
|
||
|
||
router = APIRouter(prefix="/api/settings", tags=["settings"])
|
||
|
||
|
||
@router.get("")
|
||
async def get_settings(settings_repo: SettingsRepository = Depends(get_settings_repo)):
|
||
async with get_db() as db:
|
||
settings = await settings_repo.get_all(db)
|
||
return success_response("获取设置成功", settings)
|
||
|
||
|
||
@router.post("")
|
||
async def save_settings(
|
||
request: SettingsSchema,
|
||
http_request: Request,
|
||
settings_repo: SettingsRepository = Depends(get_settings_repo),
|
||
):
|
||
async with get_db() as db:
|
||
success = await settings_repo.save(db, request.model_dump())
|
||
if not success:
|
||
raise ProxyPoolException("保存设置失败", 500)
|
||
|
||
# 热更新运行中调度器的间隔时间
|
||
scheduler = getattr(http_request.app.state, "scheduler", None)
|
||
worker_pool = getattr(http_request.app.state, "worker_pool", None)
|
||
validator = getattr(http_request.app.state, "validator", None)
|
||
|
||
if scheduler:
|
||
new_interval = request.validate_interval_minutes
|
||
if scheduler.interval_minutes != new_interval:
|
||
scheduler.interval_minutes = new_interval
|
||
logger.info(f"Scheduler interval updated to {new_interval} minutes")
|
||
|
||
# 热更新 Worker 池大小
|
||
if worker_pool and worker_pool.worker_count != request.default_concurrency:
|
||
await worker_pool.resize(request.default_concurrency)
|
||
logger.info(f"Worker pool resized to {request.default_concurrency}")
|
||
|
||
# 热更新验证器超时和并发(下次验证时生效)
|
||
if validator:
|
||
vt = float(request.validation_timeout)
|
||
validator._init_timeout = vt
|
||
# 连接阶段单独收紧:勿与 total 等同,否则死代理会在 connect 上耗满整段超时
|
||
validator._init_connect_timeout = min(
|
||
float(app_settings.validator_connect_timeout), vt
|
||
)
|
||
validator._init_max_concurrency = request.default_concurrency
|
||
if request.validation_targets is not None:
|
||
validator.update_test_urls(request.validation_targets)
|
||
# 延迟关闭旧 session:让正在验证的代理继续使用旧 session,
|
||
# 新请求会通过 _ensure_session() 自动创建使用新配置的 session
|
||
await validator.close_socks_sessions()
|
||
old_session = validator._http_session
|
||
validator._http_session = None
|
||
validator._http_connector = None
|
||
if old_session and not old_session.closed:
|
||
asyncio.create_task(old_session.close())
|
||
logger.info(f"Validator config updated: timeout={request.validation_timeout}, concurrency={request.default_concurrency}, targets={request.validation_targets}")
|
||
|
||
return success_response("保存设置成功", request.model_dump())
|