import json import os from dotenv import load_dotenv import firebase_admin from firebase_admin import messaging, credentials from google.oauth2.service_account import Credentials from pydantic import BaseModel from fastapi import HTTPException load_dotenv() class FcmRequest(BaseModel): token: str title: str body: str data: dict | None = None class FcmService: """Singleton service for sending Firebase Cloud Messages.""" _instance = None def __new__(cls): if cls._instance is None: cls._instance = super(FcmService, cls).__new__(cls) cls._instance._initialize() return cls._instance def _initialize(self): """Initialize Firebase Admin SDK once.""" service_account_json = os.getenv("FIREBASE_SERVICE_ACCOUNT_JSON") if not service_account_json: raise RuntimeError( "FIREBASE_SERVICE_ACCOUNT_JSON not found in environment variables" ) # Load service account credentials from env JSON cred_info = json.loads(service_account_json) # Initialize Firebase only once if not firebase_admin._apps: cred = credentials.Certificate(cred_info) firebase_admin.initialize_app(cred) self.admin_key = os.getenv("FIREBASE_API_ADMIN_KEY", "super-secret-admin-key") async def send_fcm(self, request: FcmRequest): """Send an FCM notification to a specific device.""" try: message = messaging.Message( notification=messaging.Notification( title=request.title, body=request.body, ), data=request.data or {}, token=request.token, android=messaging.AndroidConfig( priority="high", notification=messaging.AndroidNotification( sound="default", channel_id="feedback_notifications", # optional but recommended ), ), apns=messaging.APNSConfig( payload=messaging.APNSPayload( aps=messaging.Aps( sound="default", content_available=True, ) ), headers={ "apns-priority": "10", # 10 = immediate }, ), ) response = messaging.send(message) return {"status": "success", "message_id": response} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) async def send_to_topic(self, title: str, body: str, topic: str = "work_updates", data: dict | None = None): """Send an FCM notification to all users subscribed to a topic.""" try: message = messaging.Message( notification=messaging.Notification( title=title, body=body, ), data=data or {}, topic=topic, # 👈 send to everyone subscribed to this topic android=messaging.AndroidConfig( priority="high", notification=messaging.AndroidNotification( sound="default", channel_id="feedback_notifications", ), ), apns=messaging.APNSConfig( payload=messaging.APNSPayload( aps=messaging.Aps( sound="default", content_available=True, ) ), headers={"apns-priority": "10"}, ), ) response = messaging.send(message) return {"status": "success", "message_id": response} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) # ✅ Singleton instance fcm_service = FcmService()