add production readiness and recovery assets
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
"""Environment configuration validation for portable deployments."""
|
||||
from dataclasses import dataclass
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class ConfigError(ValueError):
|
||||
"""Raised when deployment configuration is unsafe or malformed."""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Config:
|
||||
app_env: str
|
||||
data_dir: Path
|
||||
session_secret: str
|
||||
outreach_enabled: bool
|
||||
log_level: str
|
||||
|
||||
|
||||
def _env(values, key, default=""):
|
||||
return str(values.get(key, default) or "").strip()
|
||||
|
||||
|
||||
def load_config(values=None):
|
||||
values = os.environ if values is None else values
|
||||
app_env = _env(values, "APP_ENV", "development").lower()
|
||||
if app_env not in {"development", "test", "staging", "production"}:
|
||||
raise ConfigError("APP_ENV must be development, test, staging, or production")
|
||||
data_dir = Path(_env(values, "DATA_DIR", ".") or ".").expanduser()
|
||||
if not data_dir.is_absolute():
|
||||
data_dir = (Path.cwd() / data_dir).resolve()
|
||||
if data_dir.exists() and not data_dir.is_dir():
|
||||
raise ConfigError("DATA_DIR must be a directory")
|
||||
secret = _env(values, "SESSION_SECRET")
|
||||
if app_env == "production" and (len(secret) < 32 or secret.lower() in {"change-me", "development", "dev"}):
|
||||
raise ConfigError("SESSION_SECRET must be at least 32 characters in production")
|
||||
outreach = _env(values, "AUTOMATED_OUTREACH_ENABLED", "false").lower()
|
||||
if outreach not in {"", "0", "false", "no", "off"}:
|
||||
raise ConfigError("automated outreach is disabled in this release")
|
||||
log_level = _env(values, "LOG_LEVEL", "INFO").upper()
|
||||
if log_level not in {"QUIET", "ERROR", "WARNING", "INFO", "DEBUG"}:
|
||||
raise ConfigError("LOG_LEVEL is invalid")
|
||||
return Config(app_env, data_dir, secret, False, log_level)
|
||||
Reference in New Issue
Block a user