- Add brevo.py: transactional email sending + contact list sync via Brevo API - Add token.py: stateless HMAC-signed confirmation tokens (no DB migration needed) - Update routes.py: POST /waitlist sends confirmation email, GET /waitlist/confirm verifies token - Update config.py: Brevo + confirmation settings (gracefully disabled when BREVO_API_KEY is empty) - Update .env.example with new Brevo and confirmation variables - Add httpx dependency - Add 8 new tests (token roundtrip/expiry/tamper, confirm endpoint, Brevo mock)
39 lines
1.2 KiB
Python
39 lines
1.2 KiB
Python
import secrets
|
|
|
|
from pydantic_settings import BaseSettings
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
DATABASE_URL: str = "postgresql+asyncpg://waitlist:changeme@localhost:5432/waitlist_db"
|
|
ALLOWED_ORIGINS: str = "https://adiuvai.com,https://www.adiuvai.com"
|
|
RATE_LIMIT_PER_MINUTE: int = 5
|
|
ENVIRONMENT: str = "development"
|
|
|
|
# Brevo (email)
|
|
BREVO_API_KEY: str = ""
|
|
BREVO_SENDER_EMAIL: str = "noreply@adiuvai.com"
|
|
BREVO_SENDER_NAME: str = "adiuvAI"
|
|
BREVO_LIST_ID: int = 0 # Brevo contact list ID for waitlist subscribers
|
|
|
|
# Confirmation link
|
|
CONFIRM_SECRET: str = secrets.token_hex(32) # override in production .env
|
|
CONFIRM_BASE_URL: str = "https://waitlist.adiuvai.com"
|
|
CONFIRM_TOKEN_EXPIRY_HOURS: int = 48
|
|
|
|
model_config = {"env_file": ".env", "env_file_encoding": "utf-8"}
|
|
|
|
@property
|
|
def origins_list(self) -> list[str]:
|
|
return [o.strip() for o in self.ALLOWED_ORIGINS.split(",") if o.strip()]
|
|
|
|
@property
|
|
def sync_database_url(self) -> str:
|
|
return self.DATABASE_URL.replace("+asyncpg", "+psycopg2")
|
|
|
|
@property
|
|
def brevo_configured(self) -> bool:
|
|
return bool(self.BREVO_API_KEY)
|
|
|
|
|
|
settings = Settings()
|