| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- """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()
|