import os import warnings from apiflask import APIFlask from iti.applications.common.utils.schema import custom_schema_name_resolver from iti.applications.service import init_services from .extensions import init_exts from .routes import init_routes from ..config import get_config from .events import init_event_handlers def create_app(config_name=None): """ 应用工厂函数 Args: config_name: 配置名称 ('dev', 'test', 'prod') 如果为 None,则从环境变量 FLASK_ENV 读取 Returns: Flask 应用实例 docs_ui: The UI of API documentation, one of `swagger-ui` (default), `redoc`, `elements`, `rapidoc`, and `rapipdf`. """ # 忽略 apispec 的 schema 名称冲突警告 warnings.filterwarnings( "ignore", message="Multiple schemas resolved to the name", category=UserWarning, module="apispec.ext.marshmallow.openapi", ) app = APIFlask( __name__.split(".")[0], title="iTi-Flask", version="1.0.0", json_errors=True, docs_ui="elements", ) # 加载配置 config_obj = get_config(config_name) app.config.from_object(config_obj) # 配置自定义 schema 名称解析器 # 参考:https://zh.apiflask.com/schema/#%E6%A8%A1%E5%BC%8F%E5%90%8D%E7%A7%B0%E8%A7%A3%E6%9E%90%E5%99%A8 # 用于解决循环引用和嵌套 schema 导致的命名冲突警告 app.schema_name_resolver = custom_schema_name_resolver # 确保必要的目录存在 _ensure_directories(app) # 使用第三方JWT,自定义Security,避免doc无法传递header # 等同于 SECURITY_SCHEMES 配置 app.security_schemes = { "JWT": { "type": "apiKey", "in": "header", "name": "Authorization", } } # 保护doc文档(鉴权后才可访问) # app.config['SPEC_DECORATORS'] = [jwt_required()] # app.config['DOCS_DECORATORS'] = [jwt_required()] # 初始化扩展 init_exts(app) # 初始化事件处理器 init_event_handlers(app) # 初始化路由 init_routes(app) # 初始化Services init_services(app) # 打印当前环境信息 env = config_name or os.getenv("FLASK_ENV", "dev") print(f"🚀 应用启动 - 环境: {env}") print(f"📊 数据库: {app.config.get('SQLALCHEMY_DATABASE_URI')}") return app def _ensure_directories(app): """确保必要的目录存在""" # 数据库目录(SQLite) db_uri = app.config.get("SQLALCHEMY_DATABASE_URI", "") if "sqlite:///" in db_uri and not db_uri.endswith(":memory:"): db_path = db_uri.replace("sqlite:///", "") db_dir = os.path.dirname(db_path) if db_dir and not os.path.exists(db_dir): os.makedirs(db_dir, exist_ok=True) file_storage_config = app.config.get("FILE_STORAGE", {}) local_config = file_storage_config.get("LOCAL", {}) local_path = local_config.get("base_path") if local_path: os.makedirs(local_path, exist_ok=True)