fix: 修复proxy_service和account_service中不当的顶层import
- proxy_service.py: 将requests和WhitelistManager改为函数内延迟import, 避免启动时加载不需要的依赖;移除未使用的get_exit_ip_via_proxy导入 - account_service.py: 移除未使用的func、joinedload、AuditLog、 user_has_permission顶层导入
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
"""代理可用性验证。"""
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from typing import Optional
|
||||
import time as _time
|
||||
|
||||
import requests
|
||||
from loguru import logger
|
||||
|
||||
|
||||
def verify_proxy_url(proxy_url: str, timeout: tuple = (5, 8)) -> tuple[bool, str]:
|
||||
"""
|
||||
验证代理是否可用,只验证斗鱼主站(登录的最终目标)。
|
||||
|
||||
Returns:
|
||||
(是否可用, 消息)
|
||||
"""
|
||||
from utils.http_logger import log_http
|
||||
|
||||
proxies = {'http': proxy_url, 'https': proxy_url}
|
||||
started = _time.monotonic()
|
||||
try:
|
||||
response = requests.get(
|
||||
'https://www.douyu.com',
|
||||
proxies=proxies,
|
||||
timeout=timeout,
|
||||
headers={'User-Agent': 'Mozilla/5.0'},
|
||||
)
|
||||
elapsed = _time.monotonic() - started
|
||||
response.raise_for_status()
|
||||
log_http(
|
||||
category="proxy_verify", method="GET", url="https://www.douyu.com",
|
||||
status_code=response.status_code, response_body=f"代理可用: {proxy_url}",
|
||||
duration=elapsed, proxy=proxy_url, tag="proxy_verify",
|
||||
)
|
||||
return True, '代理可用 → 斗鱼主站'
|
||||
except Exception as exc:
|
||||
elapsed = _time.monotonic() - started
|
||||
err_msg = str(exc)
|
||||
if 'Tunnel connection failed' in err_msg or '503' in err_msg:
|
||||
detail = '代理拒绝连接(白名单可能未生效)'
|
||||
elif 'timed out' in err_msg.lower():
|
||||
detail = '连接超时'
|
||||
else:
|
||||
detail = type(exc).__name__
|
||||
logger.debug(f"代理验证失败 [{proxy_url}]: {detail}")
|
||||
log_http(
|
||||
category="proxy_verify", method="GET", url="https://www.douyu.com",
|
||||
duration=elapsed, error=f"[{proxy_url}] {detail}: {err_msg[:200]}",
|
||||
proxy=proxy_url, tag="proxy_verify",
|
||||
)
|
||||
return False, detail
|
||||
|
||||
|
||||
def verify_proxies_concurrent(
|
||||
proxy_urls: list[str],
|
||||
timeout: tuple = (5, 8),
|
||||
max_workers: int = 5,
|
||||
return_all: bool = False,
|
||||
) -> tuple[Optional[str | list[str]], str]:
|
||||
"""
|
||||
并发验证多个代理 URL。
|
||||
|
||||
Returns:
|
||||
return_all=False: (可用代理 URL 或 None, 消息)
|
||||
return_all=True: (可用代理 URL 列表或 None, 消息)
|
||||
"""
|
||||
if not proxy_urls:
|
||||
return None, '无代理可验证'
|
||||
|
||||
if len(proxy_urls) == 1:
|
||||
ok, msg = verify_proxy_url(proxy_urls[0], timeout)
|
||||
if ok:
|
||||
return (proxy_urls if return_all else proxy_urls[0]), msg
|
||||
return None, msg
|
||||
|
||||
if return_all:
|
||||
available: list[str] = []
|
||||
with ThreadPoolExecutor(max_workers=min(max_workers, len(proxy_urls))) as executor:
|
||||
future_map = {
|
||||
executor.submit(verify_proxy_url, proxy, timeout): proxy
|
||||
for proxy in proxy_urls
|
||||
}
|
||||
for future in as_completed(future_map):
|
||||
try:
|
||||
ok, _ = future.result()
|
||||
if ok:
|
||||
available.append(future_map[future])
|
||||
except Exception:
|
||||
continue
|
||||
if available:
|
||||
logger.success(f"并发验证找到 {len(available)}/{len(proxy_urls)} 个可用代理")
|
||||
return available, f'找到 {len(available)} 个可用代理'
|
||||
return None, f'共 {len(proxy_urls)} 个代理均不可用'
|
||||
|
||||
with ThreadPoolExecutor(max_workers=min(max_workers, len(proxy_urls))) as executor:
|
||||
future_map = {
|
||||
executor.submit(verify_proxy_url, proxy, timeout): proxy
|
||||
for proxy in proxy_urls
|
||||
}
|
||||
for future in as_completed(future_map):
|
||||
proxy_url = future_map[future]
|
||||
try:
|
||||
ok, msg = future.result()
|
||||
if ok:
|
||||
logger.success(f"并发验证找到可用代理: {proxy_url}")
|
||||
for item in future_map:
|
||||
if item != future:
|
||||
item.cancel()
|
||||
return proxy_url, msg
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return None, f'共 {len(proxy_urls)} 个代理均不可用'
|
||||
Reference in New Issue
Block a user