osiris/tracker/push_api.py

80 lines
2.3 KiB
Python

from django.conf import settings
from django.http import HttpRequest
from ninja import Router, Schema
from ninja.errors import HttpError
from accounts.auth import AUTH
from accounts.models import PushSubscription
from tracker.push import notify_user, push_is_configured
router = Router(auth=AUTH)
class SubscriptionKeys(Schema):
p256dh: str
auth: str
class SubscribeIn(Schema):
"""Exactly the shape of a browser PushSubscription, so the client can post it as-is."""
endpoint: str
keys: SubscriptionKeys
class UnsubscribeIn(Schema):
endpoint: str
@router.post("/subscribe")
def subscribe(request: HttpRequest, payload: SubscribeIn):
"""Register this device for reminders. Re-subscribing the same endpoint is a no-op."""
if not push_is_configured():
raise HttpError(503, "Push is not configured on the server.")
PushSubscription.objects.update_or_create(
endpoint=payload.endpoint,
defaults={
"user": request.user,
"p256dh": payload.keys.p256dh,
"auth": payload.keys.auth,
"user_agent": request.headers.get("User-Agent", "")[:300],
},
)
return {"detail": "Reminders enabled on this device."}
@router.post("/unsubscribe")
def unsubscribe(request: HttpRequest, payload: UnsubscribeIn):
PushSubscription.objects.filter(
user=request.user, endpoint=payload.endpoint
).delete()
return {"detail": "Reminders disabled on this device."}
@router.post("/test")
def send_test(request: HttpRequest):
"""Push a notification right now, to prove the chain works end to end."""
if not request.user.push_subscriptions.exists():
raise HttpError(400, "No device has enabled reminders yet.")
delivered = notify_user(
request.user,
{
"title": "Osiris — test",
"body": "Reminders are working. This is what a dose reminder looks like.",
"url": "/admin/today/",
"tag": "test",
},
)
if not delivered:
raise HttpError(502, "The push service rejected the message.")
return {"detail": f"Sent to {delivered} device(s)."}
@router.get("/public-key", auth=None)
def public_key(request: HttpRequest):
"""The applicationServerKey the browser needs in order to subscribe."""
return {"public_key": settings.VAPID_PUBLIC_KEY}