diff --git a/worker_api/config.py b/worker_api/config.py index e6f522b..9223a38 100644 --- a/worker_api/config.py +++ b/worker_api/config.py @@ -2,7 +2,7 @@ import re DEFAULTS = dict( - DATABASE_URL="postgresql://admin:pechaAdmin@localhost:5434/pecha", + DATABASE_URL="postgresql://admin:pechaAdmin@localhost:5434/pecha-worker", MONGO_CONNECTION_STRING="mongodb://admin:pechaAdmin@localhost:27017/pecha?authSource=admin", MONGO_DATABASE_NAME="webuddhist", diff --git a/worker_api/notifications/services/backend_client.py b/worker_api/notifications/services/backend_client.py index b034537..a55f065 100644 --- a/worker_api/notifications/services/backend_client.py +++ b/worker_api/notifications/services/backend_client.py @@ -1,7 +1,9 @@ import httpx +from uuid import UUID + from worker_api.config import get -from worker_api.notifications.schemas import RoutineNotificationTargetsResponse +from worker_api.notifications.schemas import NotificationContent, RoutineNotificationTargetsResponse async def fetch_routine_notification_targets() -> RoutineNotificationTargetsResponse: @@ -20,3 +22,26 @@ async def fetch_routine_notification_targets() -> RoutineNotificationTargetsResp ) response.raise_for_status() return RoutineNotificationTargetsResponse.model_validate(response.json()) + + +async def fetch_plan_notification_content( + *, + user_id: UUID, + plan_id: UUID, +) -> NotificationContent: + backend_url = get("BACKEND_API_URL").rstrip("/") + if not backend_url: + raise RuntimeError("BACKEND_API_URL is not configured") + + dispatch_token = get("NOTIFICATION_DISPATCH_SECRET_TOKEN") + if not dispatch_token: + raise RuntimeError("NOTIFICATION_DISPATCH_SECRET_TOKEN is not configured") + + async with httpx.AsyncClient(timeout=30.0) as client: + response = await client.get( + f"{backend_url}/internal/plan-notification-content", + params={"user_id": str(user_id), "plan_id": str(plan_id)}, + headers={"X-Dispatch-Token": dispatch_token}, + ) + response.raise_for_status() + return NotificationContent.model_validate(response.json()) diff --git a/worker_api/notifications/services/dispatch_service.py b/worker_api/notifications/services/dispatch_service.py index fad3139..dae7fd3 100644 --- a/worker_api/notifications/services/dispatch_service.py +++ b/worker_api/notifications/services/dispatch_service.py @@ -5,7 +5,7 @@ from worker_api.db.database import SessionLocal from worker_api.notifications.repositories import reminder_repository from worker_api.notifications.schemas import DispatchDueNotificationsResponse -from worker_api.notifications.services.notification_content_service import build_notification_content +from worker_api.notifications.services.notification_content_service import resolve_notification_content from worker_api.notifications.services.push_service import ( _already_dispatched, _mark_dispatched, @@ -36,9 +36,14 @@ async def dispatch_due_notifications_service() -> DispatchDueNotificationsRespon skipped += 1 continue - title, body = build_notification_content(reminder) + content = await resolve_notification_content(reminder) try: - await send_push_notification(reminder, title, body) + await send_push_notification( + reminder, + content.title, + content.body, + content.image_url, + ) reminder_repository.mark_sent(db, reminder) reminder_repository.create_reminder( db, diff --git a/worker_api/notifications/services/notification_content_service.py b/worker_api/notifications/services/notification_content_service.py index 455e884..9695dde 100644 --- a/worker_api/notifications/services/notification_content_service.py +++ b/worker_api/notifications/services/notification_content_service.py @@ -1,5 +1,10 @@ +import logging + from worker_api.config import get from worker_api.notifications.models.reminder_models import UpcomingReminder +from worker_api.notifications.schemas import NotificationContent + +logger = logging.getLogger(__name__) def build_notification_content(reminder: UpcomingReminder) -> tuple[str, str]: @@ -15,3 +20,21 @@ def build_notification_content(reminder: UpcomingReminder) -> tuple[str, str]: body = f"Day {routine['current_day_number']}: time for your practice." return title, body + + +async def resolve_notification_content(reminder: UpcomingReminder) -> NotificationContent: + from worker_api.notifications.services.backend_client import fetch_plan_notification_content + + try: + return await fetch_plan_notification_content( + user_id=reminder.user_id, + plan_id=reminder.plan_id, + ) + except Exception: + logger.exception( + "Failed to fetch plan notification content from backend for user %s plan %s", + reminder.user_id, + reminder.plan_id, + ) + title, body = build_notification_content(reminder) + return NotificationContent(title=title, body=body, image_url=None) diff --git a/worker_api/notifications/services/push_service.py b/worker_api/notifications/services/push_service.py index 6801293..8af3fad 100644 --- a/worker_api/notifications/services/push_service.py +++ b/worker_api/notifications/services/push_service.py @@ -7,7 +7,6 @@ from worker_api.config import get, get_bool, get_int from worker_api.notifications.enums import PushPlatform from worker_api.notifications.models.reminder_models import UpcomingReminder -from worker_api.notifications.services.notification_content_service import build_notification_content from worker_api.notifications.services.push.config_loader import is_push_configured from worker_api.notifications.services.push.fcm_client import ( build_routine_notification_data, @@ -49,7 +48,12 @@ def _mark_dispatched(reminder_id: UUID) -> None: ) -async def send_push_notification(reminder: UpcomingReminder, title: str, body: str) -> None: +async def send_push_notification( + reminder: UpcomingReminder, + title: str, + body: str, + image_url: str | None = None, +) -> None: if not is_push_configured(reminder.platform): logger.warning( "Push not configured for platform %s; skipping send for reminder %s", @@ -63,11 +67,13 @@ async def send_push_notification(reminder: UpcomingReminder, title: str, body: s device_token=reminder.device_token, title=title, body=body, + image_url=image_url, data=build_routine_notification_data( session_type="PLAN", source_id=reminder.plan_id, title=title, body=body, + image_url=image_url, ), ) return