bottleneckcrm/backend/common/notifications.py
Ashwin 24390478e0 Refactor notifications handling: replace SSE with polling mechanism
- Removed the `/api/notifications/stream/` endpoint and associated SSE logic.
- Updated `Bell.svelte` and `notifications.svelte.js` to implement polling for notifications every 45 seconds.
- Introduced connection pooling configuration in environment variables documentation.
- Updated production deployment documentation to recommend WSGI over ASGI for improved database connection management.
2026-08-04 00:18:17 +05:30

68 lines
2.1 KiB
Python

"""In-app notification dispatcher.
Single call site used by every producer (watchers, mentions, escalations,
future apps). Writes a `Notification` row. That row is the whole delivery
mechanism: clients poll ``GET /api/notifications/?since=<iso>`` and pick it up
on their next tick.
This module used to also publish each new row's id on a Redis pub/sub channel
(``notif:<org_id>:<profile_id>``) to fan out to an SSE consumer. The SSE
stream was removed on 2026-08-03, which left the publish with no subscriber:
it opened a Redis connection per notification for nobody. Removed with it. If
a live transport ever comes back, it should be websockets via Channels rather
than the hand-rolled stream that was here, and it can reintroduce a fan-out
then.
"""
from __future__ import annotations
import logging
from typing import Any, Optional
from common.models import Notification, Profile
logger = logging.getLogger(__name__)
def create(
recipient: Profile,
verb: str,
*,
actor: Optional[Profile] = None,
entity: Any = None,
entity_name: str = "",
link: str = "",
data: Optional[dict] = None,
) -> Optional[Notification]:
"""Write a Notification row for the recipient.
Returns the Notification, or ``None`` when delivery was skipped because
the recipient is inactive.
``entity``: pass any model instance; ``entity_type`` is set to its class
name and ``entity_id`` to its primary key. Pass ``entity_name`` to
override the denormalized label.
"""
if recipient is None or not getattr(recipient, "is_active", True):
return None
entity_type = ""
entity_id = None
if entity is not None:
entity_type = entity.__class__.__name__
entity_id = getattr(entity, "pk", None)
if not entity_name:
entity_name = str(getattr(entity, "name", "") or "")[:255]
notif = Notification.objects.create(
org=recipient.org,
recipient=recipient,
verb=verb,
actor=actor,
entity_type=entity_type,
entity_id=entity_id,
entity_name=entity_name,
link=link,
data=data or {},
)
return notif