infrasynth-backend-kit/infrasynth/webhooks/registry.py
jcv-dev 551b42eab5 feat: production-hardening pass across the kit
Close the gaps between the documented contract (API-STANDARD, TENANCY,
ENTITLEMENTS) and the implementation, and remove committed build artifacts.

Security:
- verify + process inbound webhooks (HMAC/handler verify, size limit,
  timestamp tolerance, idempotency via InboundEvent.external_id)
- real 2FA login flow (pre-auth challenge; tokens only after verify/recovery)
- wire HybridPermission into security/audit views; add API-key rotate and
  users/<id>/permissions|roles endpoints
- tenant-scoped throttling on by default; webhook replay protection
- verify MercadoPago webhook signatures
- login brute-force guard, configurable password policy, real ALTCHA PoW

Correctness:
- apply verified billing webhooks idempotently (subscription/entitlement/
  invoice/PaymentTransaction); scheduled payment lifecycle jobs
- capture audit update diffs automatically; add audit retention purge
- working notification retries, per-channel rate limits, log retention
- pluggable virus scanner, upload-size limit, pipeline toggle
- feature rollout %/environment targeting; settings-driven registrations
- workflow guards (instance cap, route depth, self-assignment, clone on re-entry)
- wire every previously-dead INFRASYNTH_* setting; drop truly dead ones

Delivery:
- README + CHANGELOG; CI format check + coverage gate
- keep test media out of the tree; untrack .coverage, __pycache__,
  egg-info, docs/ and invoice artifacts
2026-09-24 10:41:21 -05:00

66 lines
2.2 KiB
Python

import functools
import logging
from dataclasses import dataclass, field
from django.db import transaction
from django.db.models import Q
logger = logging.getLogger(__name__)
@dataclass
class EventDefinition:
name: str
description: str = ""
example_payload: dict = field(default_factory=dict)
schema: dict = field(default_factory=dict)
class EventRegistry:
_events: dict[str, EventDefinition] = {}
@classmethod
def register(cls, event_name, *, description="", example_payload=None, schema=None):
cls._events[event_name] = EventDefinition(
name=event_name,
description=description,
example_payload=example_payload or {},
schema=schema or {},
)
logger.debug(f"Event registered: {event_name}")
@classmethod
def emit(cls, event_name, payload: dict):
from infrasynth.shared.settings_utils import get_setting
from infrasynth.tenancy.context import get_current_tenant
from . import dispatch as dispatch_module
from .models import OutboundSubscription
active_subs = OutboundSubscription.objects.filter(
Q(event_name=event_name) | Q(event_name="*"),
is_active=True,
endpoint__is_active=True,
).select_related("endpoint")
tenant = get_current_tenant()
tenant_id = str(tenant.pk) if tenant is not None else None
backend = get_setting("INFRASYNTH_WEBHOOKS", "DELIVERY_BACKEND", "celery")
def _deliver(subscription):
if backend == "sync":
dispatch_module.deliver_webhook.apply(
args=(subscription.id, event_name, payload, subscription.payload_template, tenant_id)
)
else:
dispatch_module.deliver_webhook.delay(
subscription.id, event_name, payload, subscription.payload_template, tenant_id
)
with transaction.atomic():
for sub in active_subs:
transaction.on_commit(functools.partial(_deliver, sub))
@classmethod
def get_registered_events(cls) -> dict[str, EventDefinition]:
return dict(cls._events)