Files

99 lines
3.6 KiB
Python

"""Configuration surface — the single place deployment/environment config is read.
INV-8: no deployment shape is baked into framework code. Every URL, credential, or
relay coordinate arrives through here from the environment (resolved from Secret
Manager in deployed environments). The localhost defaults match compose.yaml so a
clean `scripts/dev.sh` checkout needs no env setup.
"""
from __future__ import annotations
import os
# Dev default points at the single Postgres container compose.yaml brings up. In
# PPE/Prod the deployment supplies ECOMM_DATABASE_URL from Secret Manager (INV-8).
_DEFAULT_DATABASE_URL = "postgresql://ecomm:ecomm@localhost:5432/ecomm"
def database_url() -> str:
"""The psycopg DSN for the application database."""
return os.environ.get("ECOMM_DATABASE_URL") or _DEFAULT_DATABASE_URL
# Session signing secret. Dev default is intentionally well-known and insecure — a clean
# checkout works with no setup; deployed environments MUST supply ECOMM_SESSION_SECRET from
# Secret Manager (INV-8, §6.6). Rotating it invalidates all sessions at once (§6.2).
_DEFAULT_SESSION_SECRET = "dev-insecure-session-secret-change-me"
def session_secret() -> str:
"""The HMAC key for signed session cookies (and the one-time-code pepper)."""
return os.environ.get("ECOMM_SESSION_SECRET") or _DEFAULT_SESSION_SECRET
def cookie_secure() -> bool:
"""Whether to mark the session cookie Secure (HTTPS-only). True in deployed envs."""
return os.environ.get("ECOMM_COOKIE_SECURE", "").strip().lower() in {"1", "true", "yes", "on"}
def mailer_kind() -> str:
"""Which mailer adapter to build: 'log' (dev/tests) or 'smtp' (deployed)."""
return os.environ.get("ECOMM_MAILER") or "log"
# SMTP relay coordinates (deployed envs only; INV-8 — host/port/user/from are non-secret
# overlay values, the password is a Secret Manager reference resolved by the deploy).
def smtp_host() -> str:
return os.environ.get("ECOMM_SMTP_HOST", "")
def smtp_port() -> int:
return int(os.environ.get("ECOMM_SMTP_PORT") or "587")
def smtp_user() -> str:
return os.environ.get("ECOMM_SMTP_USER", "")
def smtp_password() -> str:
return os.environ.get("ECOMM_SMTP_PASSWORD", "")
def smtp_from() -> str:
"""The From header; defaults to the relay user."""
return os.environ.get("ECOMM_SMTP_FROM") or smtp_user()
def smtp_starttls() -> bool:
"""STARTTLS on the relay connection (default on; disable only for odd relays)."""
return os.environ.get("ECOMM_SMTP_STARTTLS", "1").strip().lower() not in {"0", "false", "no", "off"}
def objectstore_kind() -> str:
"""Which objectstore adapter to build: 'local' (dev/tests) or 'gcs' (deployed)."""
return os.environ.get("ECOMM_OBJECTSTORE_KIND") or "local"
def objectstore_bucket() -> str:
"""The GCS bucket name for the media objects (gcs adapter; deployment overlay)."""
return os.environ.get("ECOMM_OBJECTSTORE_BUCKET", "")
def objectstore_local_dir() -> str:
"""Base directory for the local-disk objectstore adapter (dev/tests)."""
return os.environ.get("ECOMM_OBJECTSTORE_DIR") or "/tmp/ecomm-objectstore"
def public_base_url() -> str:
"""Absolute origin for hosted image URLs in export (e.g. APP_URL); '' → relative."""
return (os.environ.get("APP_URL") or "").rstrip("/")
def image_fetch_allow_private() -> bool:
"""Allow image fetch from private/loopback hosts (dev/E2E fixture host only).
Default off — the SSRF guard rejects private ranges in deployed envs."""
return os.environ.get("ECOMM_IMAGE_FETCH_ALLOW_PRIVATE", "").strip().lower() in {
"1", "true", "yes", "on",
}