"""Application configuration loaded from environment variables.""" from functools import lru_cache from pydantic import field_validator from pydantic_settings import BaseSettings, SettingsConfigDict import secrets class Settings(BaseSettings): model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore") # Application APP_NAME: str = "BulkGmailMailer" ENVIRONMENT: str = "development" SECRET_KEY: str = "change-me-in-production" BASE_URL: str = "http://localhost:8000" FRONTEND_BASE_URL: str = "http://localhost:8000" # Database / Redis DATABASE_URL: str = "postgresql+psycopg://mailer:mailer_dev_pw@localhost:5432/bulkmailer" REDIS_URL: str = "redis://localhost:6379/0" CELERY_BROKER_DB: int = 1 CELERY_RESULT_DB: int = 2 # Security ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 12 SESSION_COOKIE_NAME: str = "mailer_session" CSRF_COOKIE_NAME: str = "mailer_csrf" ENCRYPTION_KEY: str = "" # Fernet key; auto-generated & persisted if empty PASSWORD_MIN_LENGTH: int = 10 # Rate limiting RATE_LIMIT_PER_MINUTE: int = 240 # Uploads MAX_UPLOAD_MB: int = 10 UPLOAD_DIR: str = "uploads" ALLOWED_ATTACHMENT_EXTS: str = "pdf,html,htm,txt,png,jpg,jpeg" ALLOWED_TEMPLATE_EXTS: str = "html,htm,txt" # Recipients / campaign limits MAX_RECIPIENTS_PER_CAMPAIGN: int = 2000 MAX_GMAIL_ACCOUNTS_PER_CAMPAIGN: int = 10 MAX_SUBJECTS: int = 1000 # Sending defaults (admin-configurable at runtime via ApplicationSettings table) DEFAULT_SEND_INTERVAL_SECONDS: float = 1.0 DEFAULT_MAX_PER_ACCOUNT_PER_CAMPAIGN: int = 200 DEFAULT_RETRIES: int = 3 DEFAULT_RETRY_DELAY_SECONDS: int = 60 MAX_ATTACHMENT_TOTAL_MB: int = 25 MAX_ATTACHMENT_PER_FILE_MB: float = 15.0 # Amount ranges (defaults; admin-configurable) AMOUNT_MIN: float = 300.0 AMOUNT_MAX: float = 1500.0 SHIPPING_MIN: float = 5.0 SHIPPING_MAX: float = 50.0 # Invoice number format tokens: # {prefix} literal prefix e.g. INV- # {r} lowercase alphanumeric random block # {R} uppercase alphanumeric random block # {d} decimal digits only # {seq4} 4-digit campaign-scoped sequence # {date} YYYYMMDD INVOICE_NUMBER_FORMAT: str = "{prefix}{R4}-{R5}-{R6}-{seq4}" INVOICE_PREFIX: str = "INV-" # Unsubscribe UNSUBSCRIBE_BASE: str = "" # defaults to BASE_URL # Test-mode integration test (real Gmail send) gate GMAIL_TEST_ACCOUNT_EMAIL: str = "" GMAIL_TEST_ACCOUNT_REFRESH_TOKEN: str = "" GMAIL_TEST_ACCOUNT_CLIENT_ID: str = "" GMAIL_TEST_ACCOUNT_CLIENT_SECRET: str = "" # OAuth client config import default file (optional) GOOGLE_OAUTH_CLIENT_JSON_PATH: str = "" @field_validator("ENCRYPTION_KEY", mode="after") @classmethod def _enc(cls, v: str) -> str: return v or "" @lru_cache def get_settings() -> Settings: return Settings() settings = get_settings()