Files
school_compare/backend/config.py
T

71 lines
2.4 KiB
Python
Raw Normal View History

2026-01-06 16:30:32 +00:00
"""
Application configuration using pydantic-settings.
Loads from environment variables and .env file.
"""
2026-01-07 16:20:49 +00:00
import secrets
2026-01-06 16:30:32 +00:00
from pathlib import Path
2026-01-06 17:15:43 +00:00
from typing import List, Optional
2026-01-06 16:30:32 +00:00
from pydantic_settings import BaseSettings
2026-01-07 16:20:49 +00:00
from pydantic import Field
2026-01-06 16:30:32 +00:00
class Settings(BaseSettings):
"""Application settings loaded from environment."""
2026-01-07 16:20:49 +00:00
2026-01-06 16:30:32 +00:00
# Paths
data_dir: Path = Path(__file__).parent.parent / "data"
frontend_dir: Path = Path(__file__).parent.parent / "frontend"
2026-01-07 16:20:49 +00:00
2026-01-06 16:30:32 +00:00
# Server
host: str = "0.0.0.0"
port: int = 80
2026-01-07 16:20:49 +00:00
debug: bool = False # Set to False in production
2026-01-06 17:15:43 +00:00
# Database
database_url: str = "postgresql://schoolcompare:schoolcompare@localhost:5432/schoolcompare"
2026-01-07 16:20:49 +00:00
# CORS - Production should only allow the actual domain
allowed_origins: List[str] = ["https://schoolcompare.co.uk"]
2026-01-06 16:30:32 +00:00
# API
default_page_size: int = 50
max_page_size: int = 100
2026-01-07 16:20:49 +00:00
# Security
admin_api_key: str = Field(default_factory=lambda: secrets.token_urlsafe(32))
rate_limit_per_minute: int = 60 # Requests per minute per IP
# A ceiling on total /api/ traffic, independent of any client identity.
# client_key trusts headers only Cloudflare can vouch for, so a caller
# reaching the origin directly could otherwise mint a fresh bucket per
# request. See GlobalRateLimitMiddleware in backend/app.py.
global_rate_limit_per_minute: int = 3000
2026-01-07 16:20:49 +00:00
rate_limit_burst: int = 10 # Allow burst of requests
max_request_size: int = 1024 * 1024 # 1MB max request size
# Typesense
typesense_url: str = "http://localhost:8108"
typesense_api_key: str = ""
# Feature flags (Unleash). An empty unleash_url disables flags entirely and
# every flag evaluates False — the correct behaviour for local development
# and CI, and the reason no test needs a running Unleash.
unleash_url: str = ""
unleash_api_token: str = ""
unleash_app_name: str = "schoolcompare-backend"
# On a named volume, so a restart during an Unleash outage keeps
# last-known state instead of reverting a released feature to dark.
unleash_cache_directory: str = "/app/.unleash"
# Analytics
ga_measurement_id: Optional[str] = "G-J0PCVT14NY" # Google Analytics 4 Measurement ID
2026-01-06 16:30:32 +00:00
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
extra = "ignore"
# Singleton instance
settings = Settings()