52 lines
1.9 KiB
Python
52 lines
1.9 KiB
Python
"""What the relay needs to know, and what it refuses to start without.
|
|||
|
|
|
||
|
|
The bearer token is the only thing standing between this service and an open
|
||
|
|
spam relay, so an unset token is a startup failure rather than a service that
|
||
|
|
accepts everything. Provider credentials are the opposite: a missing SendGrid
|
||
|
|
key must not stop SMS working, so each channel is asked at send time whether it
|
||
|
|
is configured and answers for itself.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import os
|
||
|
|
from dataclasses import dataclass, field
|
||
|
|
|
||
|
|
|
||
|
|
def _env(name: str, default: str = "") -> str:
|
||
|
|
return (os.environ.get(name, default) or "").strip()
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass(frozen=True)
|
||
|
|
class Settings:
|
||
|
|
# The shared secret every caller must present. No default: a relay that
|
||
|
|
# falls back to a well-known token is worse than one that will not boot.
|
||
|
|
token: str = field(default_factory=lambda: _env("CHANNEL_EXIT_TOKEN"))
|
||
|
|
|
||
|
|
sendgrid_key: str = field(default_factory=lambda: _env("SENDGRID_API_KEY"))
|
||
|
|
# SendGrid rejects a From that is not a verified sender, so this is an
|
||
|
|
# operator setting and never anything a caller can influence.
|
||
|
|
email_from: str = field(default_factory=lambda: _env("EMAIL_FROM"))
|
||
|
|
|
||
|
|
twilio_sid: str = field(default_factory=lambda: _env("TWILIO_ACCOUNT_SID"))
|
||
|
|
twilio_token: str = field(default_factory=lambda: _env("TWILIO_AUTH_TOKEN"))
|
||
|
|
twilio_from: str = field(default_factory=lambda: _env("TWILIO_FROM"))
|
||
|
|
|
||
|
|
timeout: float = field(default_factory=lambda: float(_env("PROVIDER_TIMEOUT", "15") or 15))
|
||
|
|
|
||
|
|
@property
|
||
|
|
def email_ready(self) -> bool:
|
||
|
|
return bool(self.sendgrid_key and self.email_from)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def sms_ready(self) -> bool:
|
||
|
|
return bool(self.twilio_sid and self.twilio_token and self.twilio_from)
|
||
|
|
|
||
|
|
def require_token(self) -> None:
|
||
|
|
if not self.token:
|
||
|
|
raise RuntimeError("CHANNEL_EXIT_TOKEN is unset; refusing to run an unauthenticated relay")
|
||
|
|
|
||
|
|
|
||
|
|
def get_settings() -> Settings:
|
||
|
|
return Settings()
|