This commit is contained in:
2026-04-08 15:26:22 +03:00
Unverified
parent 777568e5b9
commit 3079ff944e
3 changed files with 42 additions and 15 deletions
+29 -9
View File
@@ -205,6 +205,7 @@ class PushNotificationService:
title, title,
body, body,
payload_data, payload_data,
include_notification=False,
) )
logger.info( logger.info(
"FCM dm push sent recipient=%s token=%s response=%s", "FCM dm push sent recipient=%s token=%s response=%s",
@@ -275,22 +276,41 @@ class PushNotificationService:
except Exception as e: except Exception as e:
logger.error(f"Failed to send push notification to user {user_id}: {e}") logger.error(f"Failed to send push notification to user {user_id}: {e}")
def _send_fcm_to_token(self, token: str, title: str, body: str, data: dict): def _send_fcm_to_token(self, token: str, title: str, body: str, data: dict, include_notification: bool = True):
"""Send an FCM data-only push to a single device token using Firebase Admin SDK. """Send an FCM push to a single device token using Firebase Admin SDK.
Notification display is handled by the app, not FCM."""
By default this sends both notification + data payloads, but callers can disable
the notification payload for custom client-side rendering.
"""
if not self.firebase_initialized: if not self.firebase_initialized:
raise RuntimeError("Firebase Admin SDK not initialized") raise RuntimeError("Firebase Admin SDK not initialized")
try: try:
# Send only data payload - let the app handle notification display payload = {
# This prevents FCM from auto-showing notifications
msg = firebase_messaging.Message(
token=token,
data={
"title": title, "title": title,
"body": body, "body": body,
**{k: str(v) for k, v in (data or {}).items()} **{k: str(v) for k, v in (data or {}).items()}
}, }
if include_notification:
# Send notification + data payload:
# notification ensures visibility in system tray when app is background,
# data keeps app-level handling usable when app is foreground.
msg = firebase_messaging.Message(
token=token,
notification=firebase_messaging.Notification(
title=title,
body=body,
),
data=payload,
android=firebase_messaging.AndroidConfig(priority="high"),
apns=firebase_messaging.APNSConfig(headers={"apns-priority": "10"})
)
else:
# Data-only push for custom client-side rendering.
msg = firebase_messaging.Message(
token=token,
data=payload,
android=firebase_messaging.AndroidConfig(priority="high"), android=firebase_messaging.AndroidConfig(priority="high"),
apns=firebase_messaging.APNSConfig(headers={"apns-priority": "10"}) apns=firebase_messaging.APNSConfig(headers={"apns-priority": "10"})
) )
@@ -38,6 +38,7 @@ from ..service_calls import (
delete_resumable_upload_in_storage, delete_resumable_upload_in_storage,
) )
from .messaging import messagingManager, convert_dm_envelope from .messaging import messagingManager, convert_dm_envelope
from ..push_service import push_service
logger = logging.getLogger("uvicorn.error") logger = logging.getLogger("uvicorn.error")
@@ -367,6 +368,11 @@ async def send_encrypted_message(
sender_payload["client_message_id"] = request.client_message_id sender_payload["client_message_id"] = request.client_message_id
await messagingManager.send_update_to_user(dm_envelope.sender_id, "dmNew", sender_payload, db) await messagingManager.send_update_to_user(dm_envelope.sender_id, "dmNew", sender_payload, db)
try:
await push_service.send_dm_notification(db, dm_envelope, current_user)
except Exception as e:
logger.error("Failed to send push notification for DM %s: %s", dm_envelope.id, e)
return { return {
"id": dm_envelope.id, "id": dm_envelope.id,
"sender_id": dm_envelope.sender_id, "sender_id": dm_envelope.sender_id,
@@ -284,6 +284,7 @@ def convert_dm_envelope(db: Session, envelope: DMEnvelope, user_id: int | None =
"id": envelope.id, "id": envelope.id,
"senderId": envelope.sender_id, "senderId": envelope.sender_id,
"recipientId": envelope.recipient_id, "recipientId": envelope.recipient_id,
"sender_username": sender.username if sender else f"user_{envelope.sender_id}",
"iv_b64": envelope.iv_b64, "iv_b64": envelope.iv_b64,
"ciphertext_b64": envelope.ciphertext_b64, "ciphertext_b64": envelope.ciphertext_b64,
"wrapped_mek_b64": wrapped_mek_b64, "wrapped_mek_b64": wrapped_mek_b64,