SmartEDT/backend/config/settings.py

133 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""配置加载与设置模型。
优先级(高 -> 低):
1. 环境变量 SMARTEDT_CONFIG 指定的配置文件
2. 在若干候选位置寻找 config.ini兼容 PyInstaller 打包运行态)
3. 环境变量的 fallback
4. 内置默认值
"""
from __future__ import annotations
import configparser
import os
from dataclasses import dataclass
from pathlib import Path
@dataclass(frozen=True)
class ServerSettings:
"""服务监听配置。"""
host: str = "0.0.0.0"
port: int = 5000
debug: bool = False
@dataclass(frozen=True)
class FileSettings:
"""文件存储相关配置。"""
root_path: Path
@dataclass(frozen=True)
class DatabaseSettings:
"""数据库连接相关配置。"""
url: str
timescaledb: bool = True
@dataclass(frozen=True)
class UnitySettings:
host: str = "127.0.0.1"
port: int = 6000
@dataclass(frozen=True)
class AppSettings:
"""应用聚合配置。"""
server: ServerSettings
files: FileSettings
database: DatabaseSettings
unity: UnitySettings
import sys
def _find_config_file() -> Path | None:
"""尝试从若干候选位置定位 config.ini包含 PyInstaller 运行态)。"""
# Handle PyInstaller frozen state
if getattr(sys, 'frozen', False):
# If onefile, _MEIPASS. If onedir, executable dir or _internal
# With onedir, the exe is in root, internal files in _internal.
# But our spec put config in backend/config
# Check relative to executable
exe_dir = Path(sys.executable).parent
candidates = [
exe_dir / "backend" / "config" / "config.ini",
exe_dir / "_internal" / "backend" / "config" / "config.ini",
exe_dir / "config.ini",
]
for path in candidates:
if path.exists():
return path
candidates = [
Path(__file__).resolve().parent / "config.ini",
Path(__file__).resolve().parents[1] / "config.ini",
Path(__file__).resolve().parents[2] / "config.ini",
]
for path in candidates:
if path.exists():
return path
return None
def load_settings() -> AppSettings:
"""加载并返回应用配置。"""
config = configparser.ConfigParser()
config_path = os.getenv("SMARTEDT_CONFIG")
if config_path:
config.read(config_path, encoding="utf-8")
else:
found = _find_config_file()
if found:
config.read(found, encoding="utf-8")
server = ServerSettings(
host=config.get("SERVER", "host", fallback=os.getenv("SMARTEDT_HOST", "0.0.0.0")),
port=config.getint("SERVER", "port", fallback=int(os.getenv("SMARTEDT_PORT", "5000"))),
debug=config.getboolean("SERVER", "debug", fallback=os.getenv("SMARTEDT_DEBUG", "False").lower() == "true"),
)
default_root = Path(os.getenv("SMARTEDT_FILE_ROOT", "data"))
root_value = config.get("FILEPATH", "path", fallback=str(default_root))
root_path = Path(root_value)
if not root_path.is_absolute():
root_path = (Path(__file__).resolve().parents[1] / root_path).resolve()
database_url = config.get("DATABASE", "url", fallback=os.getenv("SMARTEDT_DATABASE_URL", "")).strip()
if not database_url:
database_url = "postgresql+psycopg://smartedt:CHANGE_ME@127.0.0.1:5432/smartedt"
timescaledb = config.getboolean(
"DATABASE",
"timescaledb",
fallback=os.getenv("SMARTEDT_TIMESCALEDB", "True").lower() == "true",
)
unity = UnitySettings(
host=config.get("UNITY", "host", fallback=os.getenv("SMARTEDT_UNITY_HOST", "127.0.0.1")),
port=config.getint("UNITY", "port", fallback=int(os.getenv("SMARTEDT_UNITY_PORT", "6000"))),
)
return AppSettings(
server=server,
files=FileSettings(root_path=root_path),
database=DatabaseSettings(url=database_url, timescaledb=timescaledb),
unity=unity,
)