Spaces:
Running
Running
| """ | |
| Push Service - Send web push notifications | |
| Uses pywebpush library with VAPID authentication | |
| """ | |
| import os | |
| import json | |
| from pywebpush import webpush, WebPushException | |
| import logging | |
| logger = logging.getLogger(__name__) | |
| # VAPID keys (generate with: webpush.generate_vapid_keys()) | |
| VAPID_PRIVATE_KEY = os.getenv("VAPID_PRIVATE_KEY") | |
| VAPID_PUBLIC_KEY = os.getenv("VAPID_PUBLIC_KEY") | |
| VAPID_CLAIMS = {"sub": "mailto:admin@ai-business-analyst.com"} | |
| async def send_push_notification( | |
| token: str, | |
| title: str, | |
| body: str, | |
| data: dict = None | |
| ): | |
| """Send push notification to a specific token""" | |
| if not VAPID_PRIVATE_KEY or not VAPID_PUBLIC_KEY: | |
| logger.warning("VAPID keys not configured, skipping push send") | |
| return | |
| try: | |
| # Parse subscription (token is JSON string) | |
| subscription_info = json.loads(token) | |
| # Create notification payload | |
| notification_payload = json.dumps({ | |
| "title": title, | |
| "body": body, | |
| "icon": "/logo.png", | |
| "badge": "/badge.png", | |
| "data": data or {}, | |
| "actions": [ | |
| {"action": "open", "title": "View Dashboard"}, | |
| {"action": "dismiss", "title": "Dismiss"} | |
| ] | |
| }) | |
| # Send push notification | |
| webpush( | |
| subscription_info=subscription_info, | |
| data=notification_payload, | |
| vapid_private_key=VAPID_PRIVATE_KEY, | |
| vapid_claims=VAPID_CLAIMS | |
| ) | |
| logger.info(f"Push notification sent: {title}") | |
| except WebPushException as e: | |
| logger.error(f"WebPush error: {e}") | |
| # Check if token is invalid/expired | |
| if e.response and e.response.status_code in [404, 410]: | |
| raise Exception("Token invalid or expired") | |
| raise | |
| except Exception as e: | |
| logger.error(f"Failed to send push notification: {e}") | |
| raise | |
| def generate_vapid_keys(): | |
| """ | |
| Utility to generate VAPID key pair | |
| Run this once and store keys in environment variables | |
| """ | |
| from py_vapid import Vapid | |
| from cryptography.hazmat.primitives import serialization | |
| vapid = Vapid() | |
| vapid.generate_keys() | |
| # Serialize public key | |
| public_key = vapid.public_key.public_bytes( | |
| encoding=serialization.Encoding.X962, | |
| format=serialization.PublicFormat.UncompressedPoint | |
| ) | |
| # Serialize private key | |
| private_key = vapid.private_key.private_bytes( | |
| encoding=serialization.Encoding.PEM, | |
| format=serialization.PrivateFormat.PKCS8, | |
| encryption_algorithm=serialization.NoEncryption() | |
| ) | |
| import base64 | |
| public_key_b64 = base64.urlsafe_b64encode(public_key).decode('utf-8').rstrip('=') | |
| private_key_str = private_key.decode('utf-8') | |
| print("\n" + "="*60) | |
| print("VAPID Keys Generated Successfully!") | |
| print("="*60) | |
| print("\nAdd these to your .env files:\n") | |
| print("Backend (.env):") | |
| print(f"VAPID_PUBLIC_KEY={public_key_b64}") | |
| print(f"VAPID_PRIVATE_KEY={private_key_str}") | |
| print(f"\nFrontend (.env):") | |
| print(f"VITE_VAPID_PUBLIC_KEY={public_key_b64}") | |
| print("\n" + "="*60 + "\n") | |
| if __name__ == "__main__": | |
| # Run to generate VAPID keys | |
| generate_vapid_keys() | |