feat: fpw plugins, validation/crawl perf, WS stats, test DB isolation
- 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
This commit is contained in:
@@ -3,7 +3,7 @@ import asyncio
|
||||
from contextlib import AsyncExitStack, asynccontextmanager
|
||||
from fastapi import FastAPI
|
||||
|
||||
from app.core.db import init_db, get_db
|
||||
from app.core.db import init_db, get_db, get_db_connection
|
||||
from app.core.config import settings as app_settings
|
||||
from app.core.log import logger
|
||||
from app.core.execution import AsyncWorkerPool, JobExecutor
|
||||
@@ -13,6 +13,8 @@ from app.repositories.settings_repo import SettingsRepository, DEFAULT_SETTINGS
|
||||
from app.services.validator_service import ValidatorService
|
||||
from app.services.plugin_runner import PluginRunner
|
||||
from app.services.scheduler_service import SchedulerService
|
||||
from app.api.ws_manager import ConnectionManager
|
||||
from app.api.realtime import stats_broadcaster_loop
|
||||
|
||||
settings_repo = SettingsRepository()
|
||||
proxy_repo = ProxyRepository()
|
||||
@@ -46,22 +48,50 @@ async def lifespan(app: FastAPI):
|
||||
|
||||
# 验证 WorkerPool
|
||||
async def validation_handler(proxy):
|
||||
from app.models.domain import ProxyRaw
|
||||
is_valid, latency = await validator.validate(
|
||||
proxy.ip, proxy.port, proxy.protocol
|
||||
)
|
||||
async with get_db() as db:
|
||||
if is_valid:
|
||||
await proxy_repo.insert_or_update(
|
||||
db, proxy.ip, proxy.port, proxy.protocol, score=app_settings.score_valid
|
||||
)
|
||||
if latency:
|
||||
await proxy_repo.update_response_time(db, proxy.ip, proxy.port, latency)
|
||||
async with get_db_connection() as db:
|
||||
existing = await proxy_repo.get_by_ip_port(db, proxy.ip, proxy.port)
|
||||
is_valid, latency = await validator.validate(
|
||||
proxy.ip, proxy.port, proxy.protocol
|
||||
)
|
||||
if not existing:
|
||||
return
|
||||
if existing.validated == 0:
|
||||
if is_valid:
|
||||
await proxy_repo.insert_or_update(
|
||||
db,
|
||||
proxy.ip,
|
||||
proxy.port,
|
||||
proxy.protocol,
|
||||
score=app_settings.score_valid,
|
||||
)
|
||||
if latency:
|
||||
await proxy_repo.update_response_time(
|
||||
db, proxy.ip, proxy.port, latency
|
||||
)
|
||||
else:
|
||||
await proxy_repo.delete(db, proxy.ip, proxy.port)
|
||||
else:
|
||||
await proxy_repo.update_score(
|
||||
db, proxy.ip, proxy.port, app_settings.score_invalid,
|
||||
app_settings.score_min, app_settings.score_max
|
||||
)
|
||||
if is_valid:
|
||||
await proxy_repo.insert_or_update(
|
||||
db,
|
||||
proxy.ip,
|
||||
proxy.port,
|
||||
proxy.protocol,
|
||||
score=app_settings.score_valid,
|
||||
)
|
||||
if latency:
|
||||
await proxy_repo.update_response_time(
|
||||
db, proxy.ip, proxy.port, latency
|
||||
)
|
||||
else:
|
||||
await proxy_repo.update_score(
|
||||
db,
|
||||
proxy.ip,
|
||||
proxy.port,
|
||||
app_settings.score_invalid,
|
||||
app_settings.score_min,
|
||||
app_settings.score_max,
|
||||
)
|
||||
|
||||
worker_pool = AsyncWorkerPool(
|
||||
worker_count=db_settings.get("default_concurrency", app_settings.validator_max_concurrency),
|
||||
@@ -75,7 +105,7 @@ async def lifespan(app: FastAPI):
|
||||
await stack.enter_async_context(executor)
|
||||
|
||||
# 插件运行器
|
||||
plugin_runner = PluginRunner(timeout=db_settings.get("crawl_timeout", 30))
|
||||
plugin_runner = PluginRunner()
|
||||
|
||||
# 调度器
|
||||
scheduler = SchedulerService(
|
||||
@@ -91,6 +121,9 @@ async def lifespan(app: FastAPI):
|
||||
app.state.plugin_runner = plugin_runner
|
||||
app.state.scheduler = scheduler
|
||||
|
||||
app.state.ws_manager = ConnectionManager()
|
||||
app.state.stats_broadcaster_task = asyncio.create_task(stats_broadcaster_loop(app))
|
||||
|
||||
# 启动调度器
|
||||
if db_settings.get("auto_validate", True):
|
||||
try:
|
||||
@@ -101,6 +134,13 @@ async def lifespan(app: FastAPI):
|
||||
logger.info("API server started")
|
||||
yield
|
||||
|
||||
app.state.stats_broadcaster_task.cancel()
|
||||
try:
|
||||
await app.state.stats_broadcaster_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
await app.state.ws_manager.disconnect_all()
|
||||
|
||||
# 停止调度器
|
||||
await scheduler.stop()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user