#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ HYC下载站 v2.3 - 完整增强版 主入口文件 支持镜像同步、下载加速源、系统监控、实时通信等 """ import os import sys import signal import argparse import time # PyInstaller 资源路径处理 def get_resource_path(relative_path): """获取打包后的资源路径""" if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'): # 打包后的路径 return os.path.join(sys._MEIPASS, relative_path) return os.path.join(os.path.dirname(os.path.abspath(__file__)), relative_path) # 添加项目根目录到Python路径 base_dir = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, base_dir) from core.config import ConfigManager, load_config_file, deep_merge, load_settings_with_override from core.server import MirrorServer from core.utils import parse_size from core.database import init_database, load_db_config_from_env, merge_config from core.sync_scheduler import init_database_sync from core.optimization import ( MemoryManager, LowMemoryConfig, ArchitectureDetector, check_compatibility ) def signal_handler(signum, _frame): """处理退出信号""" print(f"\n收到信号 {signum},正在关闭服务器...") import os os._exit(0) def parse_arguments(): """解析命令行参数""" parser = argparse.ArgumentParser( description='HYC下载站 v2.3 - 镜像文件服务器 + 下载加速源', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=''' 示例: # 基本启动 python main.py -p 8080 -d ./downloads # 使用配置文件 python main.py --settings settings.json --config custom.json # 认证配置 python main.py --auth-type token --auth-token your_token_here # 功能开关 python main.py --enable-monitor --enable-sync --enable-mirrors python main.py --disable-ws --disable-sse # 适合低端设备 # 镜像加速源 (通过 settings.json 配置各个镜像的启用/禁用) python main.py --enable-mirrors # 低端设备优化 python main.py --preset ultra_low python main.py --memory-limit 256M --workers 2 # 调试 python main.py --debug python main.py --debug-types http auth api v2 error # 检查系统兼容性 python main.py --check-compat ''' ) # 服务器配置 parser.add_argument('--host', default='0.0.0.0', help='监听地址 (默认: 0.0.0.0)') parser.add_argument('-p', '--port', type=int, default=8080, help='监听端口 (默认: 8080)') parser.add_argument('-d', '--base-dir', default='./downloads', help='文件存储目录 (默认: ./downloads)') parser.add_argument('--server-name', default='HYC下载站', help='服务器名称') # HTTPS配置 parser.add_argument('--ssl-cert', help='SSL证书文件路径') parser.add_argument('--ssl-key', help='SSL私钥文件路径') # 认证配置 auth_group = parser.add_argument_group('认证配置') auth_group.add_argument('--auth-type', choices=['none', 'basic', 'token'], default=None, help='认证类型: none(无), basic(基本认证), token(令牌认证)') auth_group.add_argument('--auth-user', default='admin', help='基本认证用户名') auth_group.add_argument('--auth-pass', help='基本认证密码') auth_group.add_argument('--auth-token', help='令牌认证密钥') # 功能配置 func_group = parser.add_argument_group('功能配置') func_group.add_argument('--directory-listing', type=bool, default=True, help='启用目录浏览 (默认: True)') func_group.add_argument('--enable-stats', type=bool, default=True, help='启用下载统计 (默认: True)') func_group.add_argument('--show-hash', action='store_true', help='显示文件哈希值') func_group.add_argument('--ignore-hidden', action='store_true', default=True, help='忽略隐藏文件') func_group.add_argument('--max-upload-size', default='1G', help='最大上传文件大小') func_group.add_argument('--api-version', choices=['v1', 'v2'], default='v2', help='API版本 (默认: v2)') # 实时通信配置 realtime_group = parser.add_argument_group('实时通信配置') realtime_group.add_argument('--enable-ws', action='store_true', default=True, help='启用WebSocket (默认: True)') realtime_group.add_argument('--enable-sse', action='store_true', default=True, help='启用SSE (默认: True)') # 系统监控配置 monitor_group = parser.add_argument_group('系统监控配置') monitor_group.add_argument('--enable-monitor', action='store_true', default=True, help='启用系统监控 (默认: True)') monitor_group.add_argument('--monitor-interval', type=int, default=5, help='监控数据采集间隔(秒) (默认: 5)') # 同步配置 sync_group = parser.add_argument_group('同步配置') sync_group.add_argument('--enable-sync', action='store_true', default=True, help='启用镜像同步 (默认: True)') sync_group.add_argument('--sync-config', help='同步配置文件路径') # 镜像加速源配置 mirror_group = parser.add_argument_group('镜像加速源配置') mirror_group.add_argument('--enable-mirrors', action='store_true', default=True, help='启用下载加速源 (默认: True)') # 注意: 各个镜像的启用/禁用通过 settings.json 中的 mirrors.xxx.enabled 配置 # 下载限速 rate_group = parser.add_argument_group('下载限速配置') rate_group.add_argument('--rate-limit', type=int, default=0, help='全局下载限速(字节/秒) (默认: 0=不限速)') # 低端设备优化配置 optimize_group = parser.add_argument_group('低端设备优化配置') optimize_group.add_argument('--preset', choices=['ultra_low', 'low', 'medium', 'high', 'auto'], default='auto', help='设备预设 (默认: auto)') optimize_group.add_argument('--memory-limit', default='512M', help='内存限制 (例如: 256M, 512M, 1G)') optimize_group.add_argument('--workers', type=int, default=0, help='工作进程数 (0=自动)') optimize_group.add_argument('--chunk-size', default='128K', help='文件传输块大小 (默认: 128K)') optimize_group.add_argument('--buffer-size', default='256K', help='缓冲区大小 (默认: 256K)') optimize_group.add_argument('--disable-ws', action='store_true', help='禁用WebSocket (低端设备)') optimize_group.add_argument('--disable-sse', action='store_true', help='禁用SSE (低端设备)') optimize_group.add_argument('--disable-hash', action='store_true', help='禁用文件哈希计算 (低端设备)') optimize_group.add_argument('--check-compat', action='store_true', help='检查系统兼容性后退出') # 日志配置 log_group = parser.add_argument_group('日志配置') log_group.add_argument('--access-log', help='访问日志文件路径') log_group.add_argument('--verbose', '-v', action='count', default=0, help='详细输出') log_group.add_argument('--quiet', '-q', action='store_true', help='静默模式') # 细粒度调试开关 debug_group = parser.add_argument_group('调试选项') debug_group.add_argument('--debug', '-D', action='store_true', help='启用所有调试输出') debug_group.add_argument('--debug-log', dest='debug_log_file', help='调试日志文件路径 (debug 输出将写入此文件)') debug_group.add_argument('--debug-http', action='store_true', help='调试 HTTP 请求') debug_group.add_argument('--debug-auth', action='store_true', help='调试 认证检查') debug_group.add_argument('--debug-api', action='store_true', help='调试 API 路由') debug_group.add_argument('--debug-v2', action='store_true', help='调试 V2 API') debug_group.add_argument('--debug-error', action='store_true', help='调试 错误堆栈') debug_group.add_argument('--debug-download', action='store_true', help='调试 下载记录') debug_group.add_argument('--debug-types', '--debug-list', nargs='+', metavar='TYPE', choices=['http', 'auth', 'api', 'v2', 'error', 'download'], help='指定调试类型列表 (http auth api v2 error download)') # 配置文件 parser.add_argument('--settings', '--default-config', dest='settings', help='默认配置文件路径 (settings.json)') parser.add_argument('--config', help='覆盖配置文件路径 (JSON格式,会覆盖默认配置)') return parser.parse_args() def build_config_from_args(args): """从命令行参数构建配置""" cmd_config = {} # 服务器基本配置 basic_args = ['host', 'port', 'server_name', 'ssl_cert', 'ssl_key', 'auth_type', 'auth_user', 'auth_pass', 'auth_token', 'access_log', 'verbose', 'api_version', 'directory_listing', 'enable_stats', 'show_hash', 'ignore_hidden', 'max_upload_size'] for arg_name in basic_args: arg_value = getattr(args, arg_name, None) if arg_value is not None: cmd_config[arg_name] = arg_value # Debug 配置处理 # 优先级: --debug > --debug-types > individual --debug-xxx debug_types = [] if args.debug_types: # 用户指定了类型列表 debug_types = list(args.debug_types) cmd_config['debug'] = debug_types elif args.debug_http or args.debug_auth or args.debug_api or args.debug_v2 or args.debug_error or args.debug_download: # 用户指定了单个类型 if args.debug_http: debug_types.append('http') if args.debug_auth: debug_types.append('auth') if args.debug_api: debug_types.append('api') if args.debug_v2: debug_types.append('v2') if args.debug_error: debug_types.append('error') if args.debug_download: debug_types.append('download') cmd_config['debug'] = debug_types elif args.debug: # 开启所有 cmd_config['debug'] = True else: # 未开启任何 debug cmd_config['debug'] = False # Debug 日志文件 if args.debug_log_file: cmd_config['debug_log_file'] = args.debug_log_file # 路径处理 if args.base_dir: cmd_config['base_dir'] = os.path.abspath(args.base_dir) # 实时通信 cmd_config['enable_ws'] = args.enable_ws cmd_config['enable_sse'] = args.enable_sse # 系统监控 cmd_config['enable_monitor'] = args.enable_monitor cmd_config['monitor_interval'] = args.monitor_interval # 同步 cmd_config['enable_sync'] = args.enable_sync # 镜像加速源 cmd_config['enable_mirrors'] = args.enable_mirrors # 各个镜像的启用/禁用通过 settings.json 中的 mirrors.xxx.enabled 配置 # 下载限速 cmd_config['rate_limit'] = args.rate_limit # 静默模式 if args.quiet: cmd_config['verbose'] = -1 return cmd_config def main(): """主入口函数""" args = parse_arguments() # 确定默认配置文件路径 project_root = os.path.dirname(os.path.abspath(__file__)) settings_path = args.settings or os.path.join(project_root, 'settings.json') # 打印配置来源信息 print(f"[配置加载]") print(f" 默认配置: {settings_path}") # 加载配置(优先级从低到高:默认配置 -> 覆盖配置 -> 环境变量 -> 命令行参数) # 使用深度合并,只替换覆盖配置中有的字段 # 1. 加载默认配置 (settings.json) from core.config import load_json_config default_config = load_json_config(settings_path) or {} print(f" 默认配置加载: {'成功' if default_config else '使用内联默认'}") # 2. 从覆盖配置文件加载 override_config = {} if args.config: override_config = load_json_config(args.config) or {} print(f" 覆盖配置: {args.config} ({len(override_config)} 个顶层键)") else: print(f" 覆盖配置: 未指定") # 3. 深度合并默认配置和覆盖配置 config = deep_merge(default_config, override_config) # 4. 从环境变量加载数据库配置 env_db_config = load_db_config_from_env() if env_db_config: config = deep_merge(config, env_db_config) # 5. 同步配置 if args.sync_config: sync_config = load_json_config(args.sync_config) if sync_config: if 'sync_sources' in sync_config: config['sync_sources'] = deep_merge( config.get('sync_sources', {}), sync_config['sync_sources'] ) if 'mirrors' in sync_config: config['mirrors'] = deep_merge( config.get('mirrors', {}), sync_config['mirrors'] ) # 6. 命令行参数覆盖(最高优先级) cmd_config = build_config_from_args(args) config = deep_merge(config, cmd_config) # 7. 处理 auth_token(优先从文件读取,否则生成新的) import secrets # 确定 token 文件路径 if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'): # 打包环境:使用 _MEIPASS 目录 token_file = os.path.join(sys._MEIPASS, 'auth_token.txt') else: # 非打包:使用脚本所在目录 token_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'auth_token.txt') # 尝试从文件读取 existing_token = None if os.path.exists(token_file): try: with open(token_file, 'r') as f: existing_token = f.read().strip() if existing_token: config['auth_token'] = existing_token print(f" 已从文件加载 auth_token: {config['auth_token'][:16]}...") except Exception as e: print(f" 警告: 读取 auth_token 失败: {e}") # 如果没有现有 token,生成新的 if not existing_token: config['auth_token'] = secrets.token_hex(32) print(f" 已生成新的 auth_token: {config['auth_token'][:16]}...") # 保存新的 token 到文件 try: with open(token_file, 'w') as f: f.write(config['auth_token']) print(f" 已保存 auth_token 到: {token_file}") except Exception as e: print(f" 警告: 保存 auth_token 失败: {e}") print(f" 最终配置: {len(config)} 个顶层配置项") # ==================== 系统兼容性检查 ==================== if args.check_compat: print("\n[系统兼容性检查]") compat = check_compatibility() print(f"\n兼容状态: {'✓ 通过' if compat['compatible'] else '✗ 存在问题'}") arch = ArchitectureDetector.get_architecture() print(f"\n架构信息:") print(f" - 平台: {arch['platform']}") print(f" - 机器: {arch['machine']}") print(f" - 架构: {arch['architecture']}") recommended = ArchitectureDetector.get_recommended_config() print(f"\n推荐配置:") for k, v in recommended.items(): print(f" - {k}: {v}") if compat['warnings']: print(f"\n警告:") for w in compat['warnings']: print(f" ⚠ {w}") if compat['errors']: print(f"\n错误:") for e in compat['errors']: print(f" ✗ {e}") sys.exit(0 if compat['compatible'] else 1) # ==================== 低端设备优化 ==================== print("\n[设备检测与优化]") # 检测架构 arch = ArchitectureDetector.get_architecture() # 应用低端设备配置 preset = config.get('preset', 'auto') low_mem_config = LowMemoryConfig(preset) # 获取设备信息 device_info = low_mem_config.get_device_info() # 显示设备信息 if device_info['total_ram_mb'] > 0: print(f" [硬件配置]") print(f" - 总内存: {device_info['total_ram_mb']:.0f} MB") print(f" - 可用内存: {device_info['available_ram_mb']:.0f} MB ({100-device_info['percent_used']:.1f}% 可用)") print(f" - CPU核心: {device_info['cpu_count']} 核心") print(f" - 系统架构: {arch['machine']} ({arch['architecture']})") else: print(f" - 系统架构: {arch['machine']} ({arch['architecture']})") print(f"\n [性能优化]") print(f" - 优化模式: {preset}") # 应用配置 if preset != 'auto': status = low_mem_config.get_status() print(f" - 预设方案: {status.get('description', preset)}") config = low_mem_config.apply_to_config(config) # 覆盖命令行参数 if args.memory_limit: config['memory_limit'] = parse_size(args.memory_limit) if args.workers > 0: config['workers'] = args.workers if args.chunk_size: config['chunk_size'] = parse_size(args.chunk_size) if args.buffer_size: config['buffer_size'] = parse_size(args.buffer_size) # 禁用可选功能 if args.disable_ws: config['enable_ws'] = False if args.disable_sse: config['enable_sse'] = False if args.disable_hash: config['calculate_hash'] = False # 启动内存管理器 memory_manager = MemoryManager({ 'enabled': True, 'memory_limit': config.get('memory_limit', 512 * 1024 * 1024), 'gc_interval': config.get('gc_interval', 300), # 定时GC间隔 'enable_scheduled_gc': config.get('enable_scheduled_gc', True) }) memory_manager.start() config['_memory_manager'] = memory_manager mem_status = memory_manager.get_status() print(f" - 内存限制: {mem_status['memory_limit_mb']} MB") print(f" - 工作进程: {config.get('workers', 1)}") print(f" - 传输块: {config.get('chunk_size', 128 * 1024) // 1024} KB") print(f" - GC间隔: {config.get('gc_interval', 300)}秒") print(f" - 缓存大小: {config.get('max_cache_size', 0) // (1024*1024)} MB") # 设置信号处理 signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) # 打印启动信息 print() print("╔" + "═" * 58 + "╗") print("║" + " " * 22 + "HYC下载站 v2.3" + " " * 22 + "║") print("╚" + "═" * 58 + "╝") print() print("【服务器配置】") print(f" ▶ 监听地址: {config.get('host')}:{config.get('port')}") print(f" ▶ 文件目录: {os.path.abspath(config.get('base_dir', './downloads'))}") print(f" ▶ API版本: {config.get('api_version', 'v2')}") print(f" ▶ 认证方式: {config.get('auth_type', 'none')}") max_upload = config.get('max_upload_size', '1G') if isinstance(max_upload, str): max_upload = parse_size(max_upload) else: max_upload = int(max_upload) print(f" ▶ 最大上传: {max_upload // (1024*1024)} MB") # 数据库状态 db_config = config.get('database', {}) db_enabled = db_config.get('enabled', True) print() print("【功能模块】") print(f" {'●' if config.get('enable_monitor') else '○'} 系统监控", end='') print(f" {'●' if config.get('enable_sync') else '○'} 镜像同步", end='') print(f" {'●' if config.get('enable_mirrors') else '○'} 加速源") print(f" {'●' if config.get('enable_ws') else '○'} WebSocket", end='') print(f" {'●' if config.get('enable_sse') else '○'} SSE", end='') print(f" {'●' if db_enabled else '○'} 数据库") if db_enabled: print(f" └── 类型: {db_config.get('type', 'sqlite')} | 同步间隔: {db_config.get('sync_interval', 60)}s") print() print("【性能参数】") print(f" ▶ 最大线程: {config.get('max_workers', 10)}") print(f" ▶ 工作进程: {config.get('workers', 1)}") print(f" ▶ 内存限制: {config.get('memory_limit', 512*1024*1024) // (1024*1024)} MB") print(f" ▶ 连接超时: {config.get('timeout', 30)}s") print() print("=" * 60) # 初始化数据库 db = None scheduler = None if db_enabled: print("\n[初始化数据库...]") try: db = init_database(config) # 健康检查 health = db.health_check() if health.get('healthy'): print(f" ✓ 数据库连接成功 ({db_config.get('type', 'sqlite')})") print(f" ✓ 表数量: {health.get('table_count', 0)}") else: print(f" ✗ 数据库健康检查失败: {health.get('error')}") # 检查并更新Schema if db.check_schema_version(): print(f" ✓ 数据库结构版本检查通过") # 获取统计 db_stats = db.get_stats() print(f"\n 数据库统计:") print(f" - 文件记录: {db_stats['total_files']}") print(f" - 同步记录: {db_stats['total_sync_records']}") print(f" - 缓存记录: {db_stats['total_cache_records']}") print(f" - 下载记录: {db_stats['total_download_records']}") # 将数据库实例添加到配置中 config['_db_instance'] = db except Exception as e: print(f"\n ✗ 数据库初始化失败: {e}") print(" ⚠ 服务器将继续运行,但不使用数据库功能") # 初始化同步调度器(不使用数据库) if config.get('enable_sync'): _, scheduler, _ = init_database_sync(config) scheduler.start() print(" - 同步调度器已启动") # ==================== 优雅关闭处理 ==================== import atexit def cleanup(): """服务器关闭时清理资源""" print("\n正在关闭服务器...") # 关闭数据库连接池 if db: try: db.engine.dispose() print("✓ 数据库连接已关闭") except Exception as e: print(f"✗ 关闭数据库连接时出错: {e}") # 停止同步调度器 if scheduler: try: scheduler.stop() print("✓ 同步调度器已停止") except Exception as e: print(f"✗ 停止同步调度器时出错: {e}") print("服务器已关闭") # 注册关闭处理函数 atexit.register(cleanup) # 设置服务器启动时间(用于计算运行时间) config['start_time'] = time.time() # 创建并启动服务器 try: server = MirrorServer(config) if server.start(): server.serve_forever() else: print("服务器启动失败") sys.exit(1) except Exception as e: print(f"错误: {e}") import traceback traceback.print_exc() sys.exit(1) if __name__ == "__main__": main()