Datavision / backend /services /push_service.py
DataVision CI/CD Bot
release: clean production build for HuggingFace Space
09801ca
Raw
History Blame Contribute Delete
3.32 kB
"""
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()