from functools import lru_cache from pathlib import Path from pydantic import Field from pydantic_settings import BaseSettings, SettingsConfigDict BASE_DIR = Path(__file__).resolve().parent.parent class Settings(BaseSettings): # Core data_dir: Path = Field(default=BASE_DIR / "data") db_path: Path | None = None # defaults to /bike_admin.db when None secret_key: str = Field(default="change-me", min_length=8) # CSRF/session HMAC username: str = Field(default="admin", min_length=1) password: str | None = None # REQUIRED on first run (see ensure_user) # Hosting posture is_production: bool = False # True => Secure cookies + stricter headers bind_host: str = "127.0.0.1" bind_port: int = 8000 trust_proxy: bool = True # honor X-Forwarded-For/Proto behind a reverse proxy # Sessions session_ttl_seconds: int = 7 * 24 * 3600 # Login throttling login_max_attempts: int = 8 login_window_seconds: int = 300 # Service hint: flag a bike as "book soon" when it is this close to due. service_due_soon_miles: int = 200 # Event types (preset) event_types: list[str] = [ "service", "fuel", "tyre", "chain", "brake", "repair", "other", ] model_config = SettingsConfigDict(env_prefix="BIKE_ADMIN_", env_file=".env", extra="ignore") @property def resolved_db_path(self) -> Path: return self.db_path or (self.data_dir / "bike_admin.db") @lru_cache def get_settings() -> Settings: return Settings()