mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-30 08:36:55 +09:00
Reusable, non-blocking notification layer on top of the Queue Bus: notify.send(event, to, data) records a Notification + per-channel NotificationDelivery, queues them, and a daemon consumer delivers via pluggable channel adapters (in-app, email, chat) inheriting retry, backoff and dead-letter. - Jinja2 email system: branded, dark-mode, Outlook-safe base layout + per-event templates (security/backup/monitoring/welcome/invitation). The legacy inline f-string email was removed and now renders here. - In-app center API (inbox/unread-count/mark-read) + per-user Socket.IO push. - Admin delivery log (stats/filter/retry). - Email providers (SMTP/SendGrid/Postmark/SES/Mailgun) with encrypted credentials + test; the email adapter prefers the default provider and falls back to the legacy SMTP config; surfaced in ConnectionRegistry. - NotificationService.send_all() is now a non-blocking shim that routes system alerts through the bus. Migrations 023 + 024. Tests: backend/tests/test_notification_bus.py (20). Full suite: 391 passed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
50 lines
1.6 KiB
Python
50 lines
1.6 KiB
Python
"""Channel adapter registry.
|
|
|
|
Maps a channel key -> adapter instance. ``register_adapter`` lets a plugin add
|
|
a new channel (SMS, web-push, ...) that the consumer can then deliver, exactly
|
|
like core channels.
|
|
"""
|
|
from app.notifications.channels.base import ChannelAdapter, DeliveryResult
|
|
from app.notifications.channels.inapp import InAppAdapter
|
|
from app.notifications.channels.email import EmailAdapter
|
|
from app.notifications.channels.chat import ChatAdapter
|
|
|
|
# Channels the bus knows how to deliver out of the box.
|
|
_REGISTRY = {
|
|
'inapp': InAppAdapter(),
|
|
'email': EmailAdapter(),
|
|
'discord': ChatAdapter('discord'),
|
|
'slack': ChatAdapter('slack'),
|
|
'telegram': ChatAdapter('telegram'),
|
|
'webhook': ChatAdapter('webhook'),
|
|
}
|
|
|
|
# Channels that can be targeted per-user from a user's NotificationPreferences.
|
|
# (Slack/generic-webhook are system-wide today, so they're not auto-selected for
|
|
# individual recipients — but remain deliverable if a target is supplied.)
|
|
PER_USER_CHANNELS = ('inapp', 'email', 'discord', 'telegram')
|
|
|
|
|
|
def register_adapter(adapter):
|
|
"""Register (or replace) a channel adapter. ``adapter.key`` is the channel."""
|
|
if not getattr(adapter, 'key', None):
|
|
raise ValueError('adapter must define a non-empty key')
|
|
_REGISTRY[adapter.key] = adapter
|
|
return adapter
|
|
|
|
|
|
def get_adapter(channel):
|
|
"""Return the adapter for a channel key, or None if unknown."""
|
|
return _REGISTRY.get(channel)
|
|
|
|
|
|
def known_channels():
|
|
return sorted(_REGISTRY.keys())
|
|
|
|
|
|
__all__ = [
|
|
'ChannelAdapter', 'DeliveryResult',
|
|
'register_adapter', 'get_adapter', 'known_channels',
|
|
'PER_USER_CHANNELS',
|
|
]
|