复查修复(二): 同步模块与安全遗留
- 修复 remove_sync_source 引入的 stop_sync KeyError 回归(容错 get) - stop_sync 不再删除存活线程条目(防双 worker);FTP 子目录递归传真名(不再 KeyError) - 远程文件名统一 _safe_remote_name 校验(FTP/SFTP/HTTP 防路径穿越) - temp 任务名加随机后缀防碰撞;temp 状态/线程完成后清理(防 sync_state.json 膨胀) - save_sync_state 快照+原子写(临时文件+os.replace),持锁调用不死锁 - URL 打印脱敏(user:pass@ -> ***@) - _need_sync_http size=0 不再全量重下(仅按存在性) - cron 同一分钟去重;PyPI 进度只更新当前源 - v2 start_sync 不再把 bool 当 task_id - UserRecord.to_dict 脱敏(不返回 password_hash/token),新增 to_dict_private/get_user_with_password - config_hotreload 单配置源(set+persist 与热重载一致);server.py 用 get_all() - 会话创建时顺带清理过期项;JSON stats/历史读改写加锁 - 解压目标目录先校验;FTP RETR 命令注入防护;rsync --delete 目标保护 - git/urlopen 补超时;cleanup_completed_tasks 删旧留新
This commit is contained in:
@@ -24,6 +24,37 @@ class APIv2(APIv1):
|
||||
# 管理员API处理器(始终创建,auth_type检查在装饰器中处理)
|
||||
self.admin_api = AdminAPI(config)
|
||||
|
||||
# ==================== 状态型管理器(惰性单例,跨请求保留状态) ====================
|
||||
|
||||
def _get_health_checker(self):
|
||||
"""健康检查器单例(原每请求新建导致状态丢失)"""
|
||||
if not hasattr(self, '_health_checker'):
|
||||
from core.health_check import HealthChecker
|
||||
self._health_checker = self._get_health_checker()
|
||||
return self._health_checker
|
||||
|
||||
def _get_failover_manager(self):
|
||||
"""故障转移管理器单例"""
|
||||
if not hasattr(self, '_failover_manager'):
|
||||
from core.health_check import MirrorFailoverManager
|
||||
self._failover_manager = MirrorFailoverManager(self.config)
|
||||
self._failover_manager.initialize()
|
||||
return self._failover_manager
|
||||
|
||||
def _get_restart_manager(self):
|
||||
"""优雅重启管理器单例"""
|
||||
if not hasattr(self, '_restart_manager'):
|
||||
from core.graceful_restart import GracefulRestartManager
|
||||
self._restart_manager = self._get_restart_manager()
|
||||
return self._restart_manager
|
||||
|
||||
def _get_prewarmer(self):
|
||||
"""缓存预热器单例(原每请求新建导致队列/状态丢失)"""
|
||||
if not hasattr(self, '_prewarmer'):
|
||||
from core.cache_prewarm import CachePrewarmer
|
||||
self._prewarmer = self._get_prewarmer()
|
||||
return self._prewarmer
|
||||
|
||||
def handle_request(self, handler, method, path, query_params):
|
||||
"""处理API v2请求"""
|
||||
import sys
|
||||
@@ -271,7 +302,7 @@ class APIv2(APIv1):
|
||||
elif path == 'health/stats':
|
||||
if method == 'GET':
|
||||
from core.health_check import HealthChecker
|
||||
checker = HealthChecker(self.config.get('health_check', {}))
|
||||
checker = self._get_health_checker()
|
||||
handler.send_json_response(checker.get_stats())
|
||||
else:
|
||||
handler.send_error(405)
|
||||
@@ -2283,11 +2314,12 @@ class APIv2(APIv1):
|
||||
if source_name is None:
|
||||
return super().api_start_sync(handler)
|
||||
if hasattr(handler, 'sync_manager') and handler.sync_manager:
|
||||
task_id = handler.sync_manager.start_sync(source_name)
|
||||
if task_id:
|
||||
# MirrorSyncManager.start_sync 返回 bool(True=已启动/已在运行)
|
||||
started = handler.sync_manager.start_sync(source_name)
|
||||
if started:
|
||||
handler.send_json_response({
|
||||
"success": True,
|
||||
"task_id": task_id,
|
||||
"started": True,
|
||||
"source_name": source_name
|
||||
})
|
||||
else:
|
||||
@@ -2573,7 +2605,7 @@ class APIv2(APIv1):
|
||||
from core.health_check import HealthChecker, HealthStatus
|
||||
|
||||
mirrors = self.config.get('mirrors', {})
|
||||
checker = HealthChecker(self.config.get('health_check', {}))
|
||||
checker = self._get_health_checker()
|
||||
|
||||
results = []
|
||||
for mirror_type, mirror_config in mirrors.items():
|
||||
@@ -2614,7 +2646,7 @@ class APIv2(APIv1):
|
||||
try:
|
||||
from core.health_check import HealthChecker, HealthStatus
|
||||
|
||||
checker = HealthChecker(self.config.get('health_check', {}))
|
||||
checker = self._get_health_checker()
|
||||
mirrors = self.config.get('mirrors', {})
|
||||
|
||||
# 查找源对应的镜像类型
|
||||
@@ -2659,7 +2691,7 @@ class APIv2(APIv1):
|
||||
try:
|
||||
from core.health_check import MirrorFailoverManager
|
||||
|
||||
failover = MirrorFailoverManager(self.config)
|
||||
failover = self._get_failover_manager()
|
||||
failover.initialize()
|
||||
|
||||
handler.send_json_response({
|
||||
@@ -2679,7 +2711,7 @@ class APIv2(APIv1):
|
||||
try:
|
||||
from core.health_check import MirrorFailoverManager
|
||||
|
||||
failover = MirrorFailoverManager(self.config)
|
||||
failover = self._get_failover_manager()
|
||||
failover.initialize()
|
||||
|
||||
success = failover.perform_failover(mirror_type)
|
||||
@@ -3285,7 +3317,7 @@ class APIv2(APIv1):
|
||||
|
||||
# 数据库验证
|
||||
if db:
|
||||
user = db.get_user(username)
|
||||
user = db.get_user_with_password(username)
|
||||
if user and db.verify_password(password, user['password_hash']):
|
||||
# 数据库验证成功,生成 token
|
||||
import secrets
|
||||
@@ -3388,7 +3420,7 @@ class APIv2(APIv1):
|
||||
config_pass = config.get('auth_pass', '')
|
||||
|
||||
# 验证旧密码(优先验证数据库,没有则验证配置文件)—— 强制要求,防止无旧密码改密
|
||||
user = db.get_user(username) if db else None
|
||||
user = db.get_user_with_password(username) if db else None
|
||||
if user:
|
||||
# 验证数据库密码
|
||||
if not db.verify_password(old_password, user['password_hash']):
|
||||
@@ -3985,7 +4017,7 @@ class APIv2(APIv1):
|
||||
"""获取重启状态"""
|
||||
from core.graceful_restart import GracefulRestartManager, ServerState
|
||||
|
||||
restart_manager = GracefulRestartManager(self.config.get('restart', {}))
|
||||
restart_manager = self._get_restart_manager()
|
||||
stats = restart_manager.get_stats()
|
||||
|
||||
handler.send_json_response({
|
||||
@@ -3999,7 +4031,7 @@ class APIv2(APIv1):
|
||||
"""获取待处理请求"""
|
||||
from core.graceful_restart import GracefulRestartManager
|
||||
|
||||
restart_manager = GracefulRestartManager(self.config.get('restart', {}))
|
||||
restart_manager = self._get_restart_manager()
|
||||
pending = restart_manager.get_pending_requests()
|
||||
|
||||
handler.send_json_response({
|
||||
@@ -4018,7 +4050,7 @@ class APIv2(APIv1):
|
||||
except ValueError:
|
||||
strategy = RestartStrategy.GRACEFUL
|
||||
|
||||
restart_manager = GracefulRestartManager(self.config.get('restart', {}))
|
||||
restart_manager = self._get_restart_manager()
|
||||
|
||||
# 准备重启
|
||||
prepare_result = restart_manager.prepare_restart()
|
||||
@@ -4061,7 +4093,7 @@ class APIv2(APIv1):
|
||||
except json.JSONDecodeError:
|
||||
restart_strategy = RestartStrategy.GRACEFUL
|
||||
|
||||
restart_manager = GracefulRestartManager(self.config.get('restart', {}))
|
||||
restart_manager = self._get_restart_manager()
|
||||
|
||||
# 执行重启
|
||||
result = restart_manager.perform_restart(strategy=restart_strategy)
|
||||
@@ -4072,7 +4104,7 @@ class APIv2(APIv1):
|
||||
"""立即重启服务器"""
|
||||
from core.graceful_restart import GracefulRestartManager
|
||||
|
||||
restart_manager = GracefulRestartManager(self.config.get('restart', {}))
|
||||
restart_manager = self._get_restart_manager()
|
||||
|
||||
# 获取脚本路径
|
||||
script_path = self.config.get('main_script', 'main.py')
|
||||
@@ -4088,7 +4120,7 @@ class APIv2(APIv1):
|
||||
"""获取重启历史"""
|
||||
from core.graceful_restart import GracefulRestartManager
|
||||
|
||||
restart_manager = GracefulRestartManager(self.config.get('restart', {}))
|
||||
restart_manager = self._get_restart_manager()
|
||||
history = restart_manager.get_restart_history()
|
||||
|
||||
handler.send_json_response({
|
||||
@@ -4182,7 +4214,7 @@ class APIv2(APIv1):
|
||||
"""获取缓存预热状态"""
|
||||
from core.cache_prewarm import CachePrewarmer
|
||||
|
||||
prewarmer = CachePrewarmer(self.config.get('cache_prewarm', {}))
|
||||
prewarmer = self._get_prewarmer()
|
||||
status = prewarmer.get_status()
|
||||
|
||||
handler.send_json_response(status)
|
||||
@@ -4191,7 +4223,7 @@ class APIv2(APIv1):
|
||||
"""获取缓存预热统计"""
|
||||
from core.cache_prewarm import CachePrewarmer
|
||||
|
||||
prewarmer = CachePrewarmer(self.config.get('cache_prewarm', {}))
|
||||
prewarmer = self._get_prewarmer()
|
||||
stats = prewarmer.get_stats()
|
||||
|
||||
handler.send_json_response(stats)
|
||||
@@ -4205,7 +4237,7 @@ class APIv2(APIv1):
|
||||
mirror_type = query_params.get('mirror_type', [None])[0]
|
||||
limit = int(query_params.get('limit', [50])[0])
|
||||
|
||||
prewarmer = CachePrewarmer(self.config.get('cache_prewarm', {}))
|
||||
prewarmer = self._get_prewarmer()
|
||||
items = prewarmer.get_items(status=status, mirror_type=mirror_type, limit=limit)
|
||||
|
||||
handler.send_json_response({
|
||||
@@ -4217,7 +4249,7 @@ class APIv2(APIv1):
|
||||
"""获取预热历史"""
|
||||
from core.cache_prewarm import CachePrewarmer
|
||||
|
||||
prewarmer = CachePrewarmer(self.config.get('cache_prewarm', {}))
|
||||
prewarmer = self._get_prewarmer()
|
||||
history = prewarmer.get_history()
|
||||
|
||||
handler.send_json_response({
|
||||
@@ -4234,7 +4266,7 @@ class APIv2(APIv1):
|
||||
limit = int(query_params.get('limit', [50])[0])
|
||||
priority = query_params.get('priority', ['medium'])[0]
|
||||
|
||||
prewarmer = CachePrewarmer(self.config.get('cache_prewarm', {}))
|
||||
prewarmer = self._get_prewarmer()
|
||||
|
||||
# 如果指定了镜像类型,只预热该类型
|
||||
targets = None
|
||||
@@ -4277,7 +4309,7 @@ class APIv2(APIv1):
|
||||
|
||||
from core.cache_prewarm import CachePrewarmer
|
||||
|
||||
prewarmer = CachePrewarmer(self.config.get('cache_prewarm', {}))
|
||||
prewarmer = self._get_prewarmer()
|
||||
prewarmer.add_items_batch(mirror_type, items, priority)
|
||||
|
||||
handler.send_json_response({
|
||||
@@ -4313,7 +4345,7 @@ class APIv2(APIv1):
|
||||
}, 400)
|
||||
return
|
||||
|
||||
prewarmer = CachePrewarmer(self.config.get('cache_prewarm', {}))
|
||||
prewarmer = self._get_prewarmer()
|
||||
popular = prewarmer.get_popular_items(mirror_type)
|
||||
|
||||
if limit:
|
||||
@@ -4335,7 +4367,7 @@ class APIv2(APIv1):
|
||||
|
||||
mirror_type = query_params.get('mirror_type', [None])[0]
|
||||
|
||||
prewarmer = CachePrewarmer(self.config.get('cache_prewarm', {}))
|
||||
prewarmer = self._get_prewarmer()
|
||||
|
||||
if mirror_type:
|
||||
items = prewarmer.get_popular_items(mirror_type)
|
||||
@@ -4355,7 +4387,7 @@ class APIv2(APIv1):
|
||||
"""清空预热队列"""
|
||||
from core.cache_prewarm import CachePrewarmer
|
||||
|
||||
prewarmer = CachePrewarmer(self.config.get('cache_prewarm', {}))
|
||||
prewarmer = self._get_prewarmer()
|
||||
prewarmer.clear_items()
|
||||
|
||||
handler.send_json_response({
|
||||
|
||||
Reference in New Issue
Block a user