feat: Stage 1 — backend and frontend scaffolding
Backend (tasks 1.1, 1.5–1.8): - pyproject.toml with FastAPI, Pydantic v2, aiosqlite, APScheduler 3.x, structlog, bcrypt; ruff + mypy strict configured - Pydantic Settings (BANGUI_ prefix env vars, fail-fast validation) - SQLite schema: settings, sessions, blocklist_sources, import_log; WAL mode + foreign keys; idempotent init_db() - FastAPI app factory with lifespan (DB, aiohttp session, scheduler), CORS, unhandled-exception handler, GET /api/health - Fail2BanClient: async Unix-socket wrapper using run_in_executor, custom error types, async context manager - Utility modules: ip_utils, time_utils, constants - 47 tests; ruff 0 errors; mypy --strict 0 errors Frontend (tasks 1.2–1.4): - Vite + React 18 + TypeScript strict; Fluent UI v9; ESLint + Prettier - Custom brand theme (#0F6CBD, WCAG AA contrast) with light/dark variants - Typed fetch API client (ApiError, get/post/put/del) + endpoints constants - tsc --noEmit 0 errors
This commit is contained in:
64
backend/app/config.py
Normal file
64
backend/app/config.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""Application configuration loaded from environment variables and .env file.
|
||||
|
||||
Follows pydantic-settings patterns: all values are prefixed with BANGUI_
|
||||
and validated at startup via the Settings singleton.
|
||||
"""
|
||||
|
||||
from pydantic import Field
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""BanGUI runtime configuration.
|
||||
|
||||
All fields are loaded from environment variables prefixed with ``BANGUI_``
|
||||
or from a ``.env`` file located next to the process working directory.
|
||||
The application will raise a :class:`pydantic.ValidationError` on startup
|
||||
if any required field is missing or has an invalid value.
|
||||
"""
|
||||
|
||||
database_path: str = Field(
|
||||
default="bangui.db",
|
||||
description="Filesystem path to the BanGUI SQLite application database.",
|
||||
)
|
||||
fail2ban_socket: str = Field(
|
||||
default="/var/run/fail2ban/fail2ban.sock",
|
||||
description="Path to the fail2ban Unix domain socket.",
|
||||
)
|
||||
session_secret: str = Field(
|
||||
...,
|
||||
description=(
|
||||
"Secret key used when generating session tokens. "
|
||||
"Must be unique and never committed to source control."
|
||||
),
|
||||
)
|
||||
session_duration_minutes: int = Field(
|
||||
default=60,
|
||||
ge=1,
|
||||
description="Number of minutes a session token remains valid after creation.",
|
||||
)
|
||||
timezone: str = Field(
|
||||
default="UTC",
|
||||
description="IANA timezone name used when displaying timestamps in the UI.",
|
||||
)
|
||||
log_level: str = Field(
|
||||
default="info",
|
||||
description="Application log level: debug | info | warning | error | critical.",
|
||||
)
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
env_prefix="BANGUI_",
|
||||
env_file=".env",
|
||||
env_file_encoding="utf-8",
|
||||
case_sensitive=False,
|
||||
)
|
||||
|
||||
|
||||
def get_settings() -> Settings:
|
||||
"""Return a fresh :class:`Settings` instance loaded from the environment.
|
||||
|
||||
Returns:
|
||||
A validated :class:`Settings` object. Raises :class:`pydantic.ValidationError`
|
||||
if required keys are absent or values fail validation.
|
||||
"""
|
||||
return Settings()
|
||||
Reference in New Issue
Block a user