mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-07 07:55:16 +02:00
feat: add telnyx webhook api key in telephony config (#270)
This commit is contained in:
parent
45a81c88e0
commit
01c201bf09
9 changed files with 249 additions and 13 deletions
|
|
@ -75,6 +75,34 @@ class TelephonyConfigurationClient(BaseDBClient):
|
|||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def count_telnyx_configs_missing_webhook_public_key(
|
||||
self, organization_id: int
|
||||
) -> int:
|
||||
"""Count Telnyx configs in this org with no webhook_public_key in credentials.
|
||||
|
||||
Used by the org-warnings endpoint to surface a UI nudge until customers
|
||||
paste their portal-issued public key.
|
||||
"""
|
||||
async with self.async_session() as session:
|
||||
result = await session.execute(
|
||||
select(func.count(TelephonyConfigurationModel.id)).where(
|
||||
TelephonyConfigurationModel.organization_id == organization_id,
|
||||
TelephonyConfigurationModel.provider == "telnyx",
|
||||
(
|
||||
TelephonyConfigurationModel.credentials.op("->>")(
|
||||
"webhook_public_key"
|
||||
).is_(None)
|
||||
)
|
||||
| (
|
||||
TelephonyConfigurationModel.credentials.op("->>")(
|
||||
"webhook_public_key"
|
||||
)
|
||||
== ""
|
||||
),
|
||||
)
|
||||
)
|
||||
return int(result.scalar() or 0)
|
||||
|
||||
async def list_all_telephony_configurations_by_provider(
|
||||
self, provider: str
|
||||
) -> List[TelephonyConfigurationModel]:
|
||||
|
|
|
|||
|
|
@ -87,6 +87,17 @@ class TelephonyProvidersMetadataResponse(BaseModel):
|
|||
providers: List[TelephonyProviderMetadata]
|
||||
|
||||
|
||||
class TelephonyConfigWarningsResponse(BaseModel):
|
||||
"""Aggregated telephony-configuration warning counts for the user's org.
|
||||
|
||||
Drives the page banner and nav badge that nudge customers to finish
|
||||
optional-but-recommended configuration steps. Shape is a flat dict so
|
||||
new warning types can be added without breaking the client.
|
||||
"""
|
||||
|
||||
telnyx_missing_webhook_public_key_count: int
|
||||
|
||||
|
||||
@router.get(
|
||||
"/telephony-providers/metadata",
|
||||
response_model=TelephonyProvidersMetadataResponse,
|
||||
|
|
@ -127,6 +138,27 @@ async def get_telephony_providers_metadata(user: UserModel = Depends(get_user)):
|
|||
return TelephonyProvidersMetadataResponse(providers=providers)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/telephony-config-warnings",
|
||||
response_model=TelephonyConfigWarningsResponse,
|
||||
)
|
||||
async def get_telephony_config_warnings(user: UserModel = Depends(get_user)):
|
||||
"""Return aggregated warning counts for the current org's telephony configs.
|
||||
|
||||
Today this surfaces only Telnyx configs missing ``webhook_public_key``;
|
||||
additional warning types should be added as new fields on the response.
|
||||
"""
|
||||
if not user.selected_organization_id:
|
||||
raise HTTPException(status_code=400, detail="No organization selected")
|
||||
|
||||
telnyx_missing = await db_client.count_telnyx_configs_missing_webhook_public_key(
|
||||
user.selected_organization_id
|
||||
)
|
||||
return TelephonyConfigWarningsResponse(
|
||||
telnyx_missing_webhook_public_key_count=telnyx_missing,
|
||||
)
|
||||
|
||||
|
||||
def preserve_masked_fields(provider: str, request_dict: dict, existing: dict):
|
||||
"""If the client re-submitted a masked sensitive field, restore the original."""
|
||||
for field_name in _sensitive_fields(provider):
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ def _config_loader(value: Dict[str, Any]) -> Dict[str, Any]:
|
|||
"provider": "telnyx",
|
||||
"api_key": value.get("api_key"),
|
||||
"connection_id": value.get("connection_id"),
|
||||
"webhook_public_key": value.get("webhook_public_key"),
|
||||
"from_numbers": value.get("from_numbers", []),
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +125,18 @@ _UI_METADATA = ProviderUIMetadata(
|
|||
"blank and we will auto-create one for you on save."
|
||||
),
|
||||
),
|
||||
ProviderUIField(
|
||||
name="webhook_public_key",
|
||||
label="Webhook Public Key",
|
||||
type="textarea",
|
||||
required=False,
|
||||
sensitive=False,
|
||||
description=(
|
||||
"Public key from Mission Control Portal → Keys & Credentials "
|
||||
"→ Public Key. Used to verify Telnyx webhook signatures. "
|
||||
"Without it, webhooks from Telnyx will be rejected."
|
||||
),
|
||||
),
|
||||
ProviderUIField(
|
||||
name="from_numbers",
|
||||
label="Phone Numbers",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,14 @@ class TelnyxConfigurationRequest(BaseModel):
|
|||
"stored on the configuration."
|
||||
),
|
||||
)
|
||||
webhook_public_key: Optional[str] = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"Webhook public key from Mission Control Portal → Keys & "
|
||||
"Credentials → Public Key. Used to verify Telnyx webhook "
|
||||
"signatures."
|
||||
),
|
||||
)
|
||||
# Phone numbers are managed via the dedicated phone-numbers endpoints; the
|
||||
# legacy /telephony-config POST shim still accepts them inline.
|
||||
from_numbers: List[str] = Field(
|
||||
|
|
@ -31,4 +39,5 @@ class TelnyxConfigurationResponse(BaseModel):
|
|||
provider: Literal["telnyx"] = Field(default="telnyx")
|
||||
api_key: str # Masked
|
||||
connection_id: Optional[str] = None
|
||||
webhook_public_key: Optional[str] = None
|
||||
from_numbers: List[str]
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ class TelnyxProvider(TelephonyProvider):
|
|||
def __init__(self, config: Dict[str, Any]):
|
||||
self.api_key = config.get("api_key")
|
||||
self.connection_id = config.get("connection_id")
|
||||
self.webhook_public_key = config.get("webhook_public_key")
|
||||
self.from_numbers = config.get("from_numbers", [])
|
||||
|
||||
if isinstance(self.from_numbers, str):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue