from __future__ import annotations import os from dataclasses import dataclass from pathlib import Path @dataclass(frozen=True) class Config: root_dir: Path port: int base_url: str database_path: Path employee_seed_path: Path workday_calendar_path: Path feishu_webhook_url: str feishu_webhook_secret: str feishu_app_id: str feishu_app_secret: str session_secret: str def _read_dotenv(path: Path) -> dict[str, str]: if not path.exists(): return {} values: dict[str, str] = {} for raw_line in path.read_text(encoding="utf-8").splitlines(): line = raw_line.strip() if not line or line.startswith("#") or "=" not in line: continue key, value = line.split("=", 1) values[key.strip()] = value.strip().strip('"').strip("'") return values def read_config(env: dict[str, str] | None = None) -> Config: root_dir = Path(__file__).resolve().parent.parent dotenv_values = _read_dotenv(root_dir / ".env") values = {**dotenv_values, **dict(env if env is not None else os.environ)} port = int(values.get("PORT", "8787")) if port <= 0: raise ValueError("PORT must be a positive integer") return Config( root_dir=root_dir, port=port, base_url=values.get("BASE_URL", f"http://localhost:{port}"), database_path=(root_dir / values.get("DATABASE_PATH", "data/daily-report.sqlite")).resolve(), employee_seed_path=(root_dir / values.get("EMPLOYEE_SEED_PATH", "data/employees.json")).resolve(), workday_calendar_path=(root_dir / values.get("WORKDAY_CALENDAR_PATH", "data/workday-calendar.json")).resolve(), feishu_webhook_url=values.get("FEISHU_WEBHOOK_URL", ""), feishu_webhook_secret=values.get("FEISHU_WEBHOOK_SECRET", ""), feishu_app_id=values.get("FEISHU_APP_ID", ""), feishu_app_secret=values.get("FEISHU_APP_SECRET", ""), session_secret=values.get("SESSION_SECRET") or values.get("FEISHU_APP_SECRET") or "dev-session-secret", )