Files
devplacepy/devplacepy/docs_api/groups/push.py
T
retoorandClaude Opus 5 53ddf4f233 Add push provider architecture with Apple Push Notification support
Split the push delivery library into a provider architecture. devplacepy/push
becomes a package: a PushProvider protocol with a registry, the existing Web
Push implementation moved unchanged behind it, a new APNs provider, a store
owning every push_registration access, and a delivery loop that groups a user's
subscriptions by provider, prepares each provider's payload once and sends over
a single shared client.

APNs delivers over HTTP/2 with an ES256 provider token cached per credential
fingerprint, so a worker signs at most one token per 45 minutes. Registrations
carry a hexadecimal device token; 410 and the Unregistered class of reasons soft
delete the subscription exactly like a gone Web Push endpoint.

All provider configuration is edited at /admin/services/push through the same
ConfigField surface every other subsystem uses, assembled from the registry so a
future provider needs no edit to the service. A provider that is disabled,
unconfigured or holding an unusable credential accepts no registrations and is
skipped during delivery, never failing the other providers.

POST /push.json accepts a registration for any active provider; a body without a
provider field is a Web Push body, so existing clients are unchanged. GET
/push.json keeps publicKey at the top level and adds the active providers.
push_registration gains provider and token columns, ensured in init_db with a
converging backfill; existing rows are never rewritten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:43:10 +02:00

95 lines
3.7 KiB
Python

# retoor <retoor@molodetz.nl>
from .._shared import endpoint, field
GROUP = {
"slug": "push",
"title": "Web Push",
"intro": """
# Web Push
Push notifications are delivered by one or more providers. `webpush` is the default and
implements the Web Push protocol: fetch the public VAPID key, then register a
`PushSubscription` obtained from the browser's `PushManager`. `apns` delivers to an Apple
Push Notification service device token and is only offered when an administrator has
configured it.
`GET /push.json` lists the providers that currently accept registrations. A registration
body without a `provider` field is a `webpush` registration, so existing clients need no
change.
There is no server-side unsubscribe endpoint: unsubscription is handled entirely in the
browser by calling `PushManager.unsubscribe()` on the subscription. The server stops delivering
to a subscription once its push endpoint reports it as gone. These mirror the in-app
[Notifications](/docs/notifications.html) feed.
Every endpoint follows the shared [Conventions & Errors](/docs/conventions.html) (auth, content
negotiation, pagination, status codes); see [Authentication](/docs/authentication.html) for the
four ways to sign requests.
""",
"endpoints": [
endpoint(
id="push-key",
method="GET",
path="/push.json",
title="Get the public key",
summary="Return the VAPID public key and the providers that accept registrations.",
auth="public",
sample_response={
"publicKey": "BASE64_VAPID_KEY",
"providers": {"webpush": {"publicKey": "BASE64_VAPID_KEY"}},
},
),
endpoint(
id="push-register",
method="POST",
path="/push.json",
title="Register a subscription",
summary="Register a push subscription. Sends a welcome notification.",
auth="user",
encoding="json",
interactive=False,
params=[
field(
"provider",
"json",
"string",
False,
"webpush",
"Provider to register with. Omit for webpush.",
),
field(
"endpoint",
"json",
"string",
False,
"https://fcm.googleapis.com/...",
"Subscription endpoint URL. Required for webpush.",
),
field(
"keys",
"json",
"string",
False,
'{"p256dh":"...","auth":"..."}',
"Subscription keys object. Required for webpush.",
),
field(
"token",
"json",
"string",
False,
"a1b2c3...",
"Hexadecimal device token. Required for apns.",
),
],
notes=[
'A webpush body is JSON: `{"endpoint": "...", "keys": {"p256dh": "...", "auth": "..."}}`.',
'An APNs body is JSON: `{"provider": "apns", "token": "..."}`.',
"A provider that is unknown, disabled or unconfigured returns 400.",
],
sample_response={"registered": True},
),
],
}