Files
sms-api-wrapper/sms/config.py
chelsea 1d9e7e0fab Add static API-key auth, dockerize service, publish façade docs
- sms/auth.py: verify_api_key dependency (Bearer/X-API-Key, SHA-256 hash
  compare via secrets.compare_digest, fail-closed 503 if no hash). All routes
  gated via a protected router; /health stays open for probes.
- config.py: new sms_api_key_hash setting (VOIPMS_SMS_API_KEY_HASH).
- Dockerfile + .dockerignore + docker-compose.yml: lean python:3.13-slim
  image; secrets injected via env_file, never baked in; host-localhost-only
  port mapping (SMS_PORT override); /health healthcheck.
- .env.example: committed template (placeholders only, no secrets).
- sms/docs/sms-facade.dokuwiki.txt: abstraction API reference (published to
  the apidoc wiki at voipms:sms-facade).
- README: Authentication section, Docker section, 401/503 error rows.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-04 05:09:43 +00:00

56 lines
1.7 KiB
Python

"""Configuration for the voip.ms SMS client, loaded from environment.
Set credentials before running:
export VOIPMS_API_USERNAME=...
export VOIPMS_API_PASSWORD=...
On the voip.ms side you must also whitelist this machine's public IP under
Main Menu -> SOAP / REST API -> API Security, and the sending DID must have
SMS enabled.
"""
from __future__ import annotations
from typing import Literal
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_prefix="voipms_",
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)
api_username: str = Field(..., description="voip.ms API username")
api_password: str = Field(..., description="voip.ms API password (set in API Security)")
base_url: str = Field(
"https://voip.ms/api/v1/rest.php",
description="voip.ms REST endpoint. Avoid www.voip.ms (302 drops POST bodies).",
)
dialing_mode: Literal["nanpa", "e164"] = Field(
"nanpa",
description="Number format. nanpa=10 digits; e164=+1 then 10 digits.",
)
daily_limit: int = Field(
100,
description="Per-UTC-day cap on outbound sends (matches the upstream API limit).",
ge=0,
)
sms_api_key_hash: str = Field(
"",
description=(
"Comma-separated SHA-256 hex hashes of accepted client API keys. "
"Generate a key, hash it (sha256), and store only the hash here — "
"the raw key is never persisted. Empty = fail-closed (503)."
),
)
timeout: float = Field(30.0, description="HTTP timeout in seconds.", ge=1.0)