"""DMS 一体化启动入口(PyInstaller 打包用)。 职责: 1. 设置运行时环境变量(.env、存储目录) 2. 延后导入并启动 Flask 应用 设计约束: - 必须在 ``import app`` 之前完成环境变量设置, 因为 app.py 在导入时即调用 ``init_dms(app)`` 读取配置。 - 自实现 .env 解析,避免新增 python-dotenv 依赖。 """ from __future__ import annotations import os import shutil import sys from pathlib import Path def _app_root() -> Path: """exe 同级目录(用户可见、可写)。 - PyInstaller 打包模式:``sys.executable`` 是 exe 文件本身 - 开发模式:本文件位于 ``backend/launcher.py``,root 在上一级 """ if getattr(sys, "frozen", False): return Path(sys.executable).resolve().parent return Path(__file__).resolve().parent.parent def _load_dotenv(env_path: Path) -> None: """简易 .env 解析器。 仅做 ``key=value`` 解析,``setdefault`` 不覆盖已有环境变量。 不依赖 python-dotenv,避免在 requirements.txt 中新增依赖。 """ if not env_path.exists(): return with open(env_path, "r", encoding="utf-8") as f: for raw in f: line = raw.strip() if not line or line.startswith("#") or "=" not in line: continue key, _, value = line.partition("=") key = key.strip() value = value.strip().strip('"').strip("'") os.environ.setdefault(key, value) def _ensure_runtime_dirs(root: Path) -> None: """在 exe 同级创建 dms-storage 子目录骨架,并写入环境变量。""" storage = root / "dms-storage" storage.mkdir(parents=True, exist_ok=True) for sub in ( "extracted", "original", "preview", "quarantine", "recycle", "temporary", ): (storage / sub).mkdir(exist_ok=True) # 仅当 DMS_STORAGE_ROOT 未设置或为空时写入默认值。 # 不能用 setdefault:_load_dotenv 会把 .env 中的空字符串注入环境变量, # 使 setdefault 误判为「已设置」,从而导致打包后默认存储路径失效。 current_storage = os.environ.get("DMS_STORAGE_ROOT", "").strip() if not current_storage: os.environ["DMS_STORAGE_ROOT"] = str(storage) def _bootstrap_env() -> Path: """准备运行环境,返回 app_root。""" root = _app_root() env_file = root / ".env" env_example = root / ".env.example" # 首次启动:从模板复制 .env if not env_file.exists() and env_example.exists(): shutil.copy(env_example, env_file) print("=" * 60) print("[首次启动] 已生成配置文件:") print(f" {env_file}") print("请编辑该文件填入数据库连接(DMS_DATABASE_URL)等信息,") print("保存后重新启动本程序。") print("=" * 60) _load_dotenv(env_file) _ensure_runtime_dirs(root) return root def main() -> None: root = _bootstrap_env() # 延后导入:确保上面的环境变量先生效 from app import app host = os.environ.get("DMS_HOST", "0.0.0.0") port = int(os.environ.get("DMS_PORT", "9345")) print(f"[DMS] 启动中... 访问地址: http://localhost:{port}") print(f"[DMS] 工作目录: {root}") print(f"[DMS] 按 Ctrl+C 退出") print("-" * 60) app.run(host=host, port=port, debug=False, use_reloader=False) if __name__ == "__main__": main()