102 lines
3.0 KiB
Python
102 lines
3.0 KiB
Python
|
|
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 AppSettings:
|
||
|
|
server: ServerSettings
|
||
|
|
files: FileSettings
|
||
|
|
database: DatabaseSettings
|
||
|
|
|
||
|
|
|
||
|
|
import sys
|
||
|
|
|
||
|
|
def _find_config_file() -> Path | None:
|
||
|
|
# 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",
|
||
|
|
)
|
||
|
|
|
||
|
|
return AppSettings(
|
||
|
|
server=server,
|
||
|
|
files=FileSettings(root_path=root_path),
|
||
|
|
database=DatabaseSettings(url=database_url, timescaledb=timescaledb),
|
||
|
|
)
|
||
|
|
|