init commit
This commit is contained in:
129
app/utils/settings.py
Normal file
129
app/utils/settings.py
Normal file
@@ -0,0 +1,129 @@
|
||||
"""
|
||||
Environment-aware settings for Code of Conquest.
|
||||
|
||||
- Loads environment variables from OS and `.env` (OS wins).
|
||||
- Provides repo-relative default paths for data storage.
|
||||
- Validates a few key fields (env, model backend).
|
||||
- Ensures important directories exist on first load.
|
||||
- Exposes a tiny singleton: get_settings().
|
||||
|
||||
Style:
|
||||
- Python 3.11+
|
||||
- Dataclasses (no Pydantic)
|
||||
- Docstrings + inline comments
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
class Environment(str, Enum):
|
||||
DEV = "dev"
|
||||
TEST = "test"
|
||||
PROD = "prod"
|
||||
|
||||
def _repo_root_from_here() -> Path:
|
||||
"""
|
||||
Resolve the repository root by walking up from this file.
|
||||
|
||||
This file lives at: project/app/core/utils/settings.py
|
||||
So parents[3] should be the repo root:
|
||||
parents[0] = utils
|
||||
parents[1] = core
|
||||
parents[2] = app
|
||||
parents[3] = project root
|
||||
"""
|
||||
here = Path(__file__).resolve()
|
||||
repo_root = here.parents[3]
|
||||
return repo_root
|
||||
|
||||
|
||||
@dataclass
|
||||
class Settings:
|
||||
"""
|
||||
Settings container for Code of Conquest.
|
||||
|
||||
Load order:
|
||||
1) OS environment
|
||||
2) .env file (at repo root)
|
||||
3) Defaults below
|
||||
|
||||
Paths default into the repo under ./data unless overridden.
|
||||
"""
|
||||
|
||||
# --- Core Tunables---
|
||||
env: Environment = Environment.DEV
|
||||
log_level: str = "INFO"
|
||||
flask_secret_key: str = os.getenv("FLASK_SECRET_KEY","change-me-for-prod")
|
||||
|
||||
# APPWRITE Things
|
||||
appwrite_endpoint: str = os.getenv("APPWRITE_ENDPOINT","NOT SET")
|
||||
appwrite_project_id: str = os.getenv("APPWRITE_PROJECT_ID","NOT SET")
|
||||
appwrite_api_key: str = os.getenv("APPWRITE_API_KEY","NOT SET")
|
||||
|
||||
app_name: str = "Code of Conquest"
|
||||
app_version: str = "0.0.1"
|
||||
|
||||
# --- Paths (default under ./data) ---
|
||||
repo_root: Path = field(default_factory=_repo_root_from_here)
|
||||
|
||||
# --- Build paths for convenience (not env-controlled directly) ---
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
# Basic validation
|
||||
if self.env not in (Environment.DEV, Environment.TEST, Environment.PROD):
|
||||
raise ValueError(f"Invalid COC_ENV: {self.env}")
|
||||
|
||||
@staticmethod
|
||||
def _ensure_dir(path: Path) -> None:
|
||||
if path is None:
|
||||
return
|
||||
if not path.exists():
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
# ---- Singleton loader ----
|
||||
_settings_singleton: Optional[Settings] = None
|
||||
|
||||
|
||||
def get_settings() -> Settings:
|
||||
"""
|
||||
Load settings from environment and `.env` once, then reuse.
|
||||
OS env always takes precedence over `.env`.
|
||||
|
||||
Returns:
|
||||
Settings: A process-wide singleton instance.
|
||||
"""
|
||||
global _settings_singleton
|
||||
if _settings_singleton is not None:
|
||||
return _settings_singleton
|
||||
|
||||
# Load .env from repo root
|
||||
repo_root = _repo_root_from_here()
|
||||
dotenv_path = repo_root / ".env"
|
||||
load_dotenv(dotenv_path=dotenv_path, override=False)
|
||||
|
||||
# Environment
|
||||
env_str = os.getenv("COC_ENV", "dev").strip().lower()
|
||||
if env_str == "dev":
|
||||
env_val = Environment.DEV
|
||||
elif env_str == "test":
|
||||
env_val = Environment.TEST
|
||||
elif env_str == "prod":
|
||||
env_val = Environment.PROD
|
||||
else:
|
||||
raise ValueError(f"COC_ENV must be one of dev|test|prod, got '{env_str}'")
|
||||
|
||||
# Construct settings
|
||||
_settings_singleton = Settings(
|
||||
env=env_val,
|
||||
log_level=os.getenv("LOG_LEVEL", "INFO").strip().upper(),
|
||||
)
|
||||
|
||||
return _settings_singleton
|
||||
Reference in New Issue
Block a user