Initial Aurora Core 0.1 implementation
CI / lint (push) Canceled after 0s
CI / typecheck (push) Canceled after 0s
CI / unit-tests (push) Canceled after 0s
CI / integration-tests (push) Canceled after 0s
CI / container-build (push) Canceled after 0s
CI / dependency-audit (push) Canceled after 0s
CI / lint (push) Canceled after 0s
CI / typecheck (push) Canceled after 0s
CI / unit-tests (push) Canceled after 0s
CI / integration-tests (push) Canceled after 0s
CI / container-build (push) Canceled after 0s
CI / dependency-audit (push) Canceled after 0s
Production-grade media metadata backend per docs/aurora-core-spec.md: Aurora-owned UUIDv7 identity system, normalized movie/TV/people/credits schema, provider abstraction with a TMDb adapter, an idempotent async ingestion pipeline (normalize -> match -> upsert -> provenance), a Postgres FTS/trigram search layer, a stateless FastAPI v1 API with Redis caching/rate-limiting, arq workers, dev/prod Docker Compose topologies, unit + integration + load tests, CI, and architecture/ database/API/ERD documentation. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
"""Environment-based application configuration.
|
||||
|
||||
All runtime configuration flows through this module. Nothing else in the
|
||||
codebase should read `os.environ` directly, so that configuration stays
|
||||
auditable and testable in one place.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import lru_cache
|
||||
|
||||
from pydantic import Field, field_validator
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=".env",
|
||||
env_file_encoding="utf-8",
|
||||
case_sensitive=False,
|
||||
extra="ignore",
|
||||
)
|
||||
|
||||
# --- Runtime ---
|
||||
AURORA_ENV: str = Field(default="development")
|
||||
AURORA_LOG_LEVEL: str = Field(default="INFO")
|
||||
AURORA_API_DOCS_ENABLED: bool = Field(default=True)
|
||||
|
||||
# --- Database ---
|
||||
DATABASE_URL: str = Field(
|
||||
default="postgresql+asyncpg://aurora:aurora@localhost:5432/aurora_core"
|
||||
)
|
||||
DATABASE_URL_SYNC: str = Field(
|
||||
default="postgresql+psycopg2://aurora:aurora@localhost:5432/aurora_core"
|
||||
)
|
||||
DATABASE_POOL_SIZE: int = Field(default=10)
|
||||
DATABASE_MAX_OVERFLOW: int = Field(default=20)
|
||||
# Set true when DATABASE_URL points at PgBouncer in transaction pooling
|
||||
# mode (docker-compose.prod.yml): asyncpg's server-side prepared
|
||||
# statements don't survive connections being handed to different clients
|
||||
# between transactions, so they must be disabled.
|
||||
DATABASE_BEHIND_TRANSACTION_POOLER: bool = Field(default=False)
|
||||
|
||||
# --- Redis ---
|
||||
REDIS_URL: str = Field(default="redis://localhost:6379/0")
|
||||
REDIS_REQUIRED: bool = Field(default=False)
|
||||
|
||||
# --- Security ---
|
||||
AURORA_SECRET_KEY: str = Field(default="changeme-generate-a-real-secret")
|
||||
AURORA_ADMIN_API_KEYS: str = Field(default="")
|
||||
|
||||
# --- CORS ---
|
||||
CORS_ORIGINS: str = Field(default="http://localhost:3000")
|
||||
|
||||
# --- Providers ---
|
||||
TMDB_API_KEY: str = Field(default="")
|
||||
|
||||
# --- Workers ---
|
||||
WORKER_CONCURRENCY: int = Field(default=4)
|
||||
|
||||
# --- Caching ---
|
||||
CACHE_DEFAULT_TTL: int = Field(default=300)
|
||||
|
||||
# --- Rate limiting ---
|
||||
RATE_LIMIT_ENABLED: bool = Field(default=True)
|
||||
RATE_LIMIT_PER_MINUTE: int = Field(default=120)
|
||||
|
||||
@field_validator("AURORA_ENV")
|
||||
@classmethod
|
||||
def _validate_env(cls, v: str) -> str:
|
||||
allowed = {"development", "test", "staging", "production"}
|
||||
if v not in allowed:
|
||||
raise ValueError(f"AURORA_ENV must be one of {allowed}, got {v!r}")
|
||||
return v
|
||||
|
||||
@property
|
||||
def cors_origins_list(self) -> list[str]:
|
||||
return [origin.strip() for origin in self.CORS_ORIGINS.split(",") if origin.strip()]
|
||||
|
||||
@property
|
||||
def admin_api_keys_set(self) -> set[str]:
|
||||
return {key.strip() for key in self.AURORA_ADMIN_API_KEYS.split(",") if key.strip()}
|
||||
|
||||
@property
|
||||
def is_production(self) -> bool:
|
||||
return self.AURORA_ENV == "production"
|
||||
|
||||
@property
|
||||
def docs_url(self) -> str | None:
|
||||
if not self.AURORA_API_DOCS_ENABLED:
|
||||
return None
|
||||
return "/docs"
|
||||
|
||||
@property
|
||||
def openapi_url(self) -> str | None:
|
||||
if not self.AURORA_API_DOCS_ENABLED:
|
||||
return None
|
||||
return "/openapi.json"
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_settings() -> Settings:
|
||||
return Settings()
|
||||
Reference in New Issue
Block a user