config.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  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_DATABASE_URL = (
  14. "mysql+pymysql://dms_app:change-me@127.0.0.1:3306/dms?charset=utf8mb4"
  15. )
  16. def _positive_int(name: str, default: int) -> int:
  17. raw_value = os.environ.get(name)
  18. if raw_value is None or not raw_value.strip():
  19. return default
  20. try:
  21. value = int(raw_value)
  22. except ValueError as exc:
  23. raise ValueError(f"{name}必须是正整数") from exc
  24. if value <= 0:
  25. raise ValueError(f"{name}必须是正整数")
  26. return value
  27. def load_dms_config() -> dict[str, Any]:
  28. """从环境变量加载DMS配置,不读取或覆盖现有AI配置。"""
  29. storage_value = os.environ.get("DMS_STORAGE_ROOT", "").strip()
  30. storage_root = Path(storage_value or DEFAULT_STORAGE_ROOT).expanduser()
  31. max_upload_size = _positive_int(
  32. "DMS_MAX_UPLOAD_SIZE", 100 * 1024 * 1024
  33. )
  34. max_file_size = _positive_int(
  35. "DMS_MAX_FILE_SIZE", min(50 * 1024 * 1024, max_upload_size)
  36. )
  37. if max_file_size > max_upload_size:
  38. raise ValueError("DMS_MAX_FILE_SIZE不得超过DMS_MAX_UPLOAD_SIZE")
  39. database_url = os.environ.get("DMS_DATABASE_URL", "").strip()
  40. ui_dictionary_config_value = os.environ.get(
  41. "DMS_UI_DICTIONARY_CONFIG_PATH", ""
  42. ).strip()
  43. ui_dictionary_config_path = Path(
  44. ui_dictionary_config_value or DEFAULT_UI_DICTIONARY_CONFIG_PATH
  45. ).expanduser()
  46. business_timezone = (
  47. os.environ.get("DMS_BUSINESS_TIMEZONE", "").strip()
  48. or "Asia/Shanghai"
  49. )
  50. try:
  51. ZoneInfo(business_timezone)
  52. except (ZoneInfoNotFoundError, ValueError) as exc:
  53. raise ValueError("DMS_BUSINESS_TIMEZONE必须是有效的IANA时区") from exc
  54. office_preview_enabled = (
  55. os.environ.get("DMS_OFFICE_PREVIEW_ENABLED", "true").strip().lower()
  56. in {"1", "true", "yes", "on"}
  57. )
  58. libreoffice_executable = (
  59. os.environ.get("DMS_LIBREOFFICE_EXECUTABLE", "").strip()
  60. or shutil.which("soffice")
  61. or shutil.which("libreoffice")
  62. or ""
  63. )
  64. libreoffice_timeout_seconds = _positive_int(
  65. "DMS_LIBREOFFICE_TIMEOUT_SECONDS", 60
  66. )
  67. libreoffice_max_concurrency = _positive_int(
  68. "DMS_LIBREOFFICE_MAX_CONCURRENCY", 2
  69. )
  70. return {
  71. "SQLALCHEMY_DATABASE_URI": database_url or DEFAULT_DATABASE_URL,
  72. "SQLALCHEMY_TRACK_MODIFICATIONS": False,
  73. "SQLALCHEMY_ENGINE_OPTIONS": {
  74. "isolation_level": "READ COMMITTED",
  75. "pool_pre_ping": True,
  76. "pool_recycle": 1800,
  77. "connect_args": {
  78. "init_command": "SET time_zone = '+00:00'",
  79. },
  80. },
  81. "DMS_STORAGE_ROOT": str(storage_root.resolve()),
  82. "DMS_MAX_UPLOAD_SIZE": max_upload_size,
  83. "DMS_MAX_FILE_SIZE": max_file_size,
  84. "MAX_CONTENT_LENGTH": max_upload_size,
  85. "DMS_BATCH_MAX_FILES": _positive_int("DMS_BATCH_MAX_FILES", 50),
  86. "DMS_BUSINESS_TIMEZONE": business_timezone,
  87. "DMS_JWT_SECRET": os.environ.get("DMS_JWT_SECRET", ""),
  88. "DMS_JWT_ISSUER": "dms",
  89. "DMS_JWT_EXPIRES_SECONDS": 7200,
  90. "DMS_JWT_KEEP_SIGNED_IN_EXPIRES_SECONDS": 604800,
  91. "DMS_UI_DICTIONARY_CONFIG_PATH": str(
  92. ui_dictionary_config_path.resolve()
  93. ),
  94. "DMS_OFFICE_PREVIEW_ENABLED": office_preview_enabled,
  95. "DMS_LIBREOFFICE_EXECUTABLE": libreoffice_executable,
  96. "DMS_LIBREOFFICE_TIMEOUT_SECONDS": libreoffice_timeout_seconds,
  97. "DMS_LIBREOFFICE_MAX_CONCURRENCY": libreoffice_max_concurrency,
  98. }