fix
All checks were successful
Deploy to Development / build-and-deploy (push) Successful in 22s
Deploy to Production / build-and-deploy (push) Successful in 4s

This commit is contained in:
EvanChal
2026-01-28 22:31:17 +01:00
parent 8ff0f22682
commit 7ca168c34c
14 changed files with 576 additions and 145 deletions

View File

@@ -6,18 +6,63 @@ from models.notification import PushSubscription
from models.user import User
from schemas.notification import PushSubscriptionCreate, VapidPublicKeyResponse
from utils.security import get_current_active_user
from utils.push_service import is_push_configured, send_push_to_user
import base64
router = APIRouter()
def get_vapid_public_key_for_web_push():
"""
Convertit la clé publique VAPID au format attendu par Web Push.
Les clés VAPID peuvent être au format:
1. Base64url brut (65 octets décodés) - Format attendu par Web Push
2. PEM/DER complet (commence par MFk...) - Doit être converti
Web Push attend la clé publique non compressée (65 octets = 0x04 + X + Y)
"""
public_key = settings.VAPID_PUBLIC_KEY
if not public_key:
return None
# Nettoyer la clé (enlever les éventuels espaces/newlines)
public_key = public_key.strip()
# Si la clé commence par MFk, c'est au format DER/SubjectPublicKeyInfo
# On doit extraire les 65 derniers octets
if public_key.startswith('MFk'):
try:
# Décoder le DER
# Ajouter le padding si nécessaire
padding = 4 - len(public_key) % 4
if padding != 4:
public_key += '=' * padding
der_bytes = base64.b64decode(public_key)
# La clé publique non compressée est les 65 derniers octets
# (SubjectPublicKeyInfo header + la clé)
if len(der_bytes) >= 65:
raw_key = der_bytes[-65:]
# Ré-encoder en base64url sans padding
return base64.urlsafe_b64encode(raw_key).rstrip(b'=').decode('ascii')
except Exception as e:
print(f"Erreur conversion clé VAPID: {e}")
return public_key
return public_key
@router.get("/vapid-public-key", response_model=VapidPublicKeyResponse)
async def get_vapid_public_key(current_user: User = Depends(get_current_active_user)):
async def get_vapid_public_key_endpoint(current_user: User = Depends(get_current_active_user)):
"""Get the VAPID public key for push notifications."""
if not settings.VAPID_PUBLIC_KEY:
public_key = get_vapid_public_key_for_web_push()
if not public_key:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="VAPID keys not configured on server"
)
return {"public_key": settings.VAPID_PUBLIC_KEY}
return {"public_key": public_key}
@router.post("/subscribe")
async def subscribe_push(
@@ -66,3 +111,60 @@ async def unsubscribe_push(
return {"message": "Unsubscribed successfully"}
@router.post("/test")
async def test_push_notification(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""Send a test push notification to the current user."""
if not is_push_configured():
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Push notifications not configured. Check VAPID keys."
)
# Vérifier que l'utilisateur a au moins un abonnement
sub_count = db.query(PushSubscription).filter(
PushSubscription.user_id == current_user.id
).count()
if sub_count == 0:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Aucun appareil enregistré pour les notifications push"
)
# Envoyer la notification de test
sent = send_push_to_user(
db,
current_user.id,
"🔔 Test de notification",
"Les notifications push fonctionnent correctement !",
"/"
)
return {
"message": f"Notification de test envoyée à {sent} appareil(s)",
"devices_registered": sub_count,
"sent_successfully": sent
}
@router.get("/status")
async def get_push_status(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""Get the push notification configuration status."""
sub_count = db.query(PushSubscription).filter(
PushSubscription.user_id == current_user.id
).count()
return {
"configured": is_push_configured(),
"vapid_public_key_set": bool(settings.VAPID_PUBLIC_KEY),
"vapid_private_key_set": bool(settings.VAPID_PRIVATE_KEY),
"user_subscriptions": sub_count
}