""" MiroFish Backend - Flask应用工厂 """ import os import warnings # 抑制 multiprocessing resource_tracker 的警告(来自第三方库如 transformers) # 需要在所有其他导入之前设置 warnings.filterwarnings("ignore", message=".*resource_tracker.*") from flask import Flask, request from flask_cors import CORS from .config import Config from .services.supabase_jobs import sync_enabled as supabase_sync_enabled from .utils.logger import setup_logger, get_logger def create_app(config_class=Config): """Flask应用工厂函数""" app = Flask(__name__) app.config.from_object(config_class) # 设置JSON编码:确保中文直接显示(而不是 \uXXXX 格式) # Flask >= 2.3 使用 app.json.ensure_ascii,旧版本使用 JSON_AS_ASCII 配置 if hasattr(app, 'json') and hasattr(app.json, 'ensure_ascii'): app.json.ensure_ascii = False # 设置日志 logger = setup_logger('mirofish') # 只在 reloader 子进程中打印启动信息(避免 debug 模式下打印两次) is_reloader_process = os.environ.get('WERKZEUG_RUN_MAIN') == 'true' debug_mode = app.config.get('DEBUG', False) should_log_startup = not debug_mode or is_reloader_process if should_log_startup: logger.info("=" * 50) logger.info("MiroFish Backend 启动中...") logger.info("=" * 50) # CORS: allow custom pipeline auth headers from static sites / other origins CORS( app, resources={ r"/api/*": { "origins": "*", "allow_headers": [ "Content-Type", "Authorization", "X-Mirofish-User-Id", "X-Pipeline-Client-Secret", ], } }, ) # 注册模拟进程清理函数(确保服务器关闭时终止所有模拟进程) from .services.simulation_runner import SimulationRunner SimulationRunner.register_cleanup() if should_log_startup: logger.info("已注册模拟进程清理函数") # 请求日志中间件 @app.before_request def log_request(): logger = get_logger('mirofish.request') logger.debug(f"请求: {request.method} {request.path}") if request.content_type and 'json' in request.content_type: logger.debug(f"请求体: {request.get_json(silent=True)}") @app.after_request def log_response(response): logger = get_logger('mirofish.request') logger.debug(f"响应: {response.status_code}") return response # 注册蓝图 from .api import graph_bp, simulation_bp, report_bp, pipeline_bp, billing_bp app.register_blueprint(graph_bp, url_prefix='/api/graph') app.register_blueprint(simulation_bp, url_prefix='/api/simulation') app.register_blueprint(report_bp, url_prefix='/api/report') app.register_blueprint(pipeline_bp, url_prefix='/api/pipeline') app.register_blueprint(billing_bp, url_prefix='/api/billing') # 健康检查 @app.route('/health') def health(): return { 'status': 'ok', 'service': 'MiroFish Backend', 'supabase_sync': supabase_sync_enabled(), 'pipeline_client_secret_configured': bool((Config.PIPELINE_CLIENT_SECRET or '').strip()), } if should_log_startup: if supabase_sync_enabled(): logger.info("Supabase cloud sync: ENABLED (simulation_runs + reports)") else: logger.warning( "Supabase cloud sync: DISABLED — set SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY " "so runs appear in Past simulations and in the database." ) logger.info("MiroFish Backend 启动完成") return app