Files
ProxyPool/app/api/routes/settings.py
祀梦 7bc6d4e4de feat: JSON 配置、质量分与仪表盘,及设置与爬取流程
- 后端改为 config/app.json;pytest 使用 config/app.test.json 与 set_config_file,不再依赖环境变量;移除 pydantic-settings。

- 前端 API/WebSocket 由 config/webui.json 经 Vite define 注入。

- 代理分数按延迟与随机取用次数计算,新增 use_count 与 proxy_scoring;保存设置时同步调度器启停。

- 仪表盘双饼图(可用/待验证协议);设置页去掉调度器启停按钮并移动立即验证;爬取全部结束后自动提交全量验证。

- 删除 script/settings_maintain.py(此前已标记删除)。

Made-with: Cursor
2026-04-05 16:08:32 +08:00

85 lines
3.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""设置相关路由"""
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")
want_run = bool(request.auto_validate)
if want_run and not scheduler.running:
try:
await scheduler.start()
except Exception as e:
logger.error(f"Failed to start scheduler after settings save: {e}")
elif not want_run and scheduler.running:
try:
await scheduler.stop()
except Exception as e:
logger.error(f"Failed to stop scheduler after settings save: {e}")
# 热更新 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())