replace screenshot pipeline and update admin

This commit is contained in:
2025-12-31 16:50:35 +08:00
parent 2d98ab66a3
commit 41ead4bead
25 changed files with 443 additions and 2250 deletions

View File

@@ -350,7 +350,7 @@ def get_system_stats():
@admin_api_bp.route("/browser_pool/stats", methods=["GET"])
@admin_required
def get_browser_pool_stats():
"""获取浏览器池状态"""
"""获取截图线程池状态"""
try:
from browser_pool_worker import get_browser_worker_pool
@@ -408,8 +408,8 @@ def get_browser_pool_stats():
}
)
except Exception as e:
logger.exception(f"[AdminAPI] 获取浏览器池状态失败: {e}")
return jsonify({"error": "获取浏览器池状态失败"}), 500
logger.exception(f"[AdminAPI] 获取截图线程池状态失败: {e}")
return jsonify({"error": "获取截图线程池状态失败"}), 500
@admin_api_bp.route("/docker_stats", methods=["GET"])
@@ -619,7 +619,7 @@ def update_system_config_api():
if new_max_screenshot_concurrent is not None:
if not isinstance(new_max_screenshot_concurrent, int) or new_max_screenshot_concurrent < 1:
return jsonify({"error": "截图并发数必须大于0建议根据服务器配置设置每个浏览器约占用200MB内存"}), 400
return jsonify({"error": "截图并发数必须大于0建议根据服务器配置设置wkhtmltoimage 资源占用较低"}), 400
if schedule_time is not None:
import re
@@ -672,6 +672,14 @@ def update_system_config_api():
max_global=int(new_config.get("max_concurrent_global", old_config.get("max_concurrent_global", 2))),
max_per_user=int(new_config.get("max_concurrent_per_account", old_config.get("max_concurrent_per_account", 1))),
)
if new_max_screenshot_concurrent is not None:
try:
from browser_pool_worker import resize_browser_worker_pool
if resize_browser_worker_pool(int(new_config.get("max_screenshot_concurrent", new_max_screenshot_concurrent))):
logger.info(f"截图线程池并发已更新为: {new_config.get('max_screenshot_concurrent')}")
except Exception as pool_error:
logger.warning(f"截图线程池并发更新失败: {pool_error}")
except Exception:
pass

View File

@@ -295,6 +295,21 @@ def get_ip_risk(ip):
)
@security_bp.route("/api/admin/security/ip-risk/clear", methods=["POST"])
@admin_required
def clear_ip_risk():
"""清除指定IP的风险分"""
data = _parse_json()
ip_text = str(data.get("ip") or "").strip()
if not ip_text:
return jsonify({"error": "ip不能为空"}), 400
if not scorer.reset_ip_score(ip_text):
return jsonify({"error": "清理失败"}), 400
return jsonify({"success": True, "ip": _truncate(ip_text, 64), "risk_score": 0})
@security_bp.route("/api/admin/security/user-risk/<int:user_id>", methods=["GET"])
@admin_required
def get_user_risk(user_id):
@@ -331,4 +346,3 @@ def cleanup_expired():
pool_stats = None
return jsonify({"success": True, "pool_stats": pool_stats})

View File

@@ -11,7 +11,6 @@ from crypto_utils import encrypt_password as encrypt_account_password
from flask import Blueprint, jsonify, request
from flask_login import current_user, login_required
from services.accounts_service import load_user_accounts
from services.browser_manager import init_browser_manager_async
from services.browse_types import BROWSE_TYPE_SHOULD_READ, normalize_browse_type, validate_browse_type
from services.client_log import log_to_client
from services.models import Account
@@ -230,10 +229,6 @@ def start_account(account_id):
if not browse_type:
return jsonify({"error": "浏览类型无效"}), 400
enable_screenshot = data.get("enable_screenshot", True)
if enable_screenshot:
# 异步初始化浏览器环境,避免首次下载/安装 Chromium 阻塞请求导致“网页无响应”
init_browser_manager_async()
ok, message = submit_account_task(
user_id=user_id,
account_id=account_id,
@@ -308,9 +303,6 @@ def manual_screenshot(account_id):
account.last_browse_type = browse_type
# 异步初始化浏览器环境,避免首次下载/安装 Chromium 阻塞请求
init_browser_manager_async()
threading.Thread(
target=take_screenshot_for_account,
args=(user_id, account_id, browse_type, "manual_screenshot"),
@@ -336,10 +328,6 @@ def batch_start_accounts():
if not account_ids:
return jsonify({"error": "请选择要启动的账号"}), 400
if enable_screenshot:
# 异步初始化浏览器环境,避免首次下载/安装 Chromium 阻塞请求
init_browser_manager_async()
started = []
failed = []