config.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. """DMS环境配置。"""
  2. from __future__ import annotations
  3. import os
  4. from pathlib import Path
  5. from typing import Any
  6. from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
  7. import shutil
  8. BACKEND_ROOT = Path(__file__).resolve().parents[1]
  9. DEFAULT_STORAGE_ROOT = BACKEND_ROOT / "dms-storage"
  10. DEFAULT_UI_DICTIONARY_CONFIG_PATH = (
  11. BACKEND_ROOT / "dms" / "resources" / "ui-dictionaries.zh-CN.json"
  12. )
  13. DEFAULT_DOCUMENT_RELATION_PRESET_CONFIG_PATH = (
  14. BACKEND_ROOT / "dms" / "resources" / "document-relation-presets.zh-CN.json"
  15. )
  16. # 当前数据库:达梦 DM(dm+dmPython 方言,默认端口 5236,schema 由用户 DMS 决定)。
  17. # 原 MySQL 默认链接(保留备查,回切时恢复):
  18. # "mysql+pymysql://dms_app:change-me@127.0.0.1:3306/dms?charset=utf8mb4"
  19. DEFAULT_DATABASE_URL = "dm+dmPython://DMS:change-me@127.0.0.1:5236"
  20. def _positive_int(name: str, default: int) -> int:
  21. raw_value = os.environ.get(name)
  22. if raw_value is None or not raw_value.strip():
  23. return default
  24. try:
  25. value = int(raw_value)
  26. except ValueError as exc:
  27. raise ValueError(f"{name}必须是正整数") from exc
  28. if value <= 0:
  29. raise ValueError(f"{name}必须是正整数")
  30. return value
  31. def load_dms_config() -> dict[str, Any]:
  32. """从环境变量加载DMS配置,不读取或覆盖现有AI配置。"""
  33. storage_value = os.environ.get("DMS_STORAGE_ROOT", "").strip()
  34. storage_root = Path(storage_value or DEFAULT_STORAGE_ROOT).expanduser()
  35. max_upload_size = _positive_int(
  36. "DMS_MAX_UPLOAD_SIZE", 100 * 1024 * 1024
  37. )
  38. max_file_size = _positive_int(
  39. "DMS_MAX_FILE_SIZE", min(50 * 1024 * 1024, max_upload_size)
  40. )
  41. if max_file_size > max_upload_size:
  42. raise ValueError("DMS_MAX_FILE_SIZE不得超过DMS_MAX_UPLOAD_SIZE")
  43. database_url = os.environ.get("DMS_DATABASE_URL", "").strip()
  44. ui_dictionary_config_value = os.environ.get(
  45. "DMS_UI_DICTIONARY_CONFIG_PATH", ""
  46. ).strip()
  47. ui_dictionary_config_path = Path(
  48. ui_dictionary_config_value or DEFAULT_UI_DICTIONARY_CONFIG_PATH
  49. ).expanduser()
  50. relation_preset_config_value = os.environ.get(
  51. "DMS_DOCUMENT_RELATION_PRESET_CONFIG_PATH", ""
  52. ).strip()
  53. relation_preset_config_path = Path(
  54. relation_preset_config_value
  55. or DEFAULT_DOCUMENT_RELATION_PRESET_CONFIG_PATH
  56. ).expanduser()
  57. business_timezone = (
  58. os.environ.get("DMS_BUSINESS_TIMEZONE", "").strip()
  59. or "Asia/Shanghai"
  60. )
  61. try:
  62. ZoneInfo(business_timezone)
  63. except (ZoneInfoNotFoundError, ValueError) as exc:
  64. raise ValueError("DMS_BUSINESS_TIMEZONE必须是有效的IANA时区") from exc
  65. office_preview_enabled = (
  66. os.environ.get("DMS_OFFICE_PREVIEW_ENABLED", "true").strip().lower()
  67. in {"1", "true", "yes", "on"}
  68. )
  69. libreoffice_executable = (
  70. os.environ.get("DMS_LIBREOFFICE_EXECUTABLE", "").strip()
  71. or shutil.which("soffice")
  72. or shutil.which("libreoffice")
  73. or ""
  74. )
  75. libreoffice_timeout_seconds = _positive_int(
  76. "DMS_LIBREOFFICE_TIMEOUT_SECONDS", 60
  77. )
  78. libreoffice_max_concurrency = _positive_int(
  79. "DMS_LIBREOFFICE_MAX_CONCURRENCY", 2
  80. )
  81. pdf_viewer_engine_raw = (
  82. os.environ.get("DMS_PDF_VIEWER_ENGINE", "iframe").strip().lower()
  83. )
  84. pdf_viewer_engine = (
  85. pdf_viewer_engine_raw if pdf_viewer_engine_raw in {"iframe", "pdfjs"} else "iframe"
  86. )
  87. resolved_database_url = database_url or DEFAULT_DATABASE_URL
  88. # init_command(SET time_zone)为 pymysql 专属参数,达梦 dmPython 不接受;
  89. # 仅在 MySQL 链接时下发。
  90. connect_args = (
  91. {"init_command": "SET time_zone = '+00:00'"}
  92. if resolved_database_url.startswith(("mysql", "jdbc:mysql"))
  93. else {}
  94. )
  95. return {
  96. "SQLALCHEMY_DATABASE_URI": resolved_database_url,
  97. "SQLALCHEMY_TRACK_MODIFICATIONS": False,
  98. "SQLALCHEMY_ENGINE_OPTIONS": {
  99. "isolation_level": "READ COMMITTED",
  100. "pool_pre_ping": True,
  101. "pool_recycle": 1800,
  102. "connect_args": connect_args,
  103. },
  104. "DMS_STORAGE_ROOT": str(storage_root.resolve()),
  105. "DMS_MAX_UPLOAD_SIZE": max_upload_size,
  106. "DMS_MAX_FILE_SIZE": max_file_size,
  107. "MAX_CONTENT_LENGTH": max_upload_size,
  108. "DMS_BATCH_MAX_FILES": _positive_int("DMS_BATCH_MAX_FILES", 50),
  109. "DMS_BUSINESS_TIMEZONE": business_timezone,
  110. "DMS_JWT_SECRET": os.environ.get("DMS_JWT_SECRET", ""),
  111. "DMS_JWT_ISSUER": "dms",
  112. "DMS_JWT_EXPIRES_SECONDS": 7200,
  113. "DMS_JWT_KEEP_SIGNED_IN_EXPIRES_SECONDS": 604800,
  114. "DMS_UI_DICTIONARY_CONFIG_PATH": str(
  115. ui_dictionary_config_path.resolve()
  116. ),
  117. "DMS_DOCUMENT_RELATION_PRESET_CONFIG_PATH": str(
  118. relation_preset_config_path.resolve()
  119. ),
  120. "DMS_OFFICE_PREVIEW_ENABLED": office_preview_enabled,
  121. "DMS_LIBREOFFICE_EXECUTABLE": libreoffice_executable,
  122. "DMS_LIBREOFFICE_TIMEOUT_SECONDS": libreoffice_timeout_seconds,
  123. "DMS_LIBREOFFICE_MAX_CONCURRENCY": libreoffice_max_concurrency,
  124. "DMS_PDF_VIEWER_ENGINE": pdf_viewer_engine,
  125. }