From b36d7aba9a6ffbf43647f0cfeb8436bc1d86411f Mon Sep 17 00:00:00 2001 From: Chris Fulljames Date: Fri, 1 Aug 2025 17:21:50 -0400 Subject: [PATCH] Periodically renew push subscriptions --- src/littlesongplace/push_notifications.py | 40 +++++++++++++++---- src/littlesongplace/static/nav.js | 44 +++++++++++++++++++++ src/littlesongplace/templates/activity.html | 24 +---------- 3 files changed, 78 insertions(+), 30 deletions(-) diff --git a/src/littlesongplace/push_notifications.py b/src/littlesongplace/push_notifications.py index 2a17dea..a5c535e 100644 --- a/src/littlesongplace/push_notifications.py +++ b/src/littlesongplace/push_notifications.py @@ -24,13 +24,30 @@ def subscribe(): # Request must contain valid subscription JSON abort(400) - row = db.query( - """ - INSERT INTO users_push_subscriptions (userid, subscription, settings) - VALUES (?, ?, ?) - RETURNING subid - """, - [g.userid, json.dumps(request.json), 0], expect_one=True) + subid = request.args.get("subid", None) + existing_sub = None + if subid: + existing_sub = db.query( + "SELECT * FROM users_push_subscriptions WHERE subid = ? AND userid = ?", + [subid, g.userid]) + + if existing_sub: + row = db.query( + """ + UPDATE users_push_subscriptions + SET userid = ?, subscription = ? + WHERE subid = ? AND userid = ? + RETURNING subid + """, + [g.userid, json.dumps(request.json), subid, g.userid], expect_one=True) + else: + row = db.query( + """ + INSERT INTO users_push_subscriptions (userid, subscription, settings) + VALUES (?, ?, ?) + RETURNING subid + """, + [g.userid, json.dumps(request.json), 0], expect_one=True) db.commit() current_app.logger.info(f"{g.username} registered push subscription") @@ -58,6 +75,15 @@ def update_subscription(subid): return {"status": "success", "subid": row["subid"]} +@bp.get("/vapid-public-key") +def vapid_public_key(): + try: + with open(datadir.get_vapid_public_key_path(), "r") as keyfile: + key = keyfile.read().strip() + return {"status": "ok", "public_key": key} + except OSError: + return {"status": "error"} + @bp.get("/settings") @auth.requires_login def get_settings(): diff --git a/src/littlesongplace/static/nav.js b/src/littlesongplace/static/nav.js index efb7e9b..5a16cbd 100644 --- a/src/littlesongplace/static/nav.js +++ b/src/littlesongplace/static/nav.js @@ -258,3 +258,47 @@ function updateImageColors() { }); } +async function periodicPushSync() { + console.log("sync"); + if (!("serviceWorker" in navigator)) { + return; // No service woker available + } + const subid = window.localStorage.getItem("subid"); + console.log(subid); + if (subid) { + await syncPushSubscription(); + } +} + +async function syncPushSubscription() { + const registration = await navigator.serviceWorker.getRegistration(); + let subscription = await registration.pushManager.getSubscription(); + if (!subscription) + { + // Subscribe via browser's push service + const data = await fetch("/push-notifications/vapid-public-key").then((r) => { return r.json() }); + const vapid_public_key = data.public_key; + const options = {userVisibleOnly: true, applicationServerKey: vapid_public_key}; + subscription = await registration.pushManager.subscribe(options); + console.log(JSON.stringify(subscription)); + } + + // Register (or update) subscription with LSP server + const subid = window.localStorage.getItem("subid"); + const params = subid ? `?subid=${subid}` : ""; + const response = await fetch( + `/push-notifications/subscribe${params}`, { + method: "post", + headers: {"Content-Type": "application/json"}, + body: JSON.stringify(subscription) + } + ); + + const rspJson = await response.json(); + console.log("Subscription ID:", rspJson.subid); + window.localStorage.setItem("subid", rspJson.subid); +} + +periodicPushSync(); +setInterval(periodicPushSync, 10000); + diff --git a/src/littlesongplace/templates/activity.html b/src/littlesongplace/templates/activity.html index f28531a..ed82c76 100644 --- a/src/littlesongplace/templates/activity.html +++ b/src/littlesongplace/templates/activity.html @@ -80,29 +80,7 @@ async function enablePushNotifications() { if (permission === "granted") { // Subscribe to push notifications (if we don't already have an active subscription) try { - const registration = await navigator.serviceWorker.getRegistration(); - const existingSubscription = await registration.pushManager.getSubscription(); - if (!existingSubscription || !window.localStorage.getItem("subid")) - { - // Subscribe via browser's push service - const vapid_public_key = "{{ vapid_public_key }}"; - const options = {userVisibleOnly: true, applicationServerKey: vapid_public_key}; - const subscription = await registration.pushManager.subscribe(options); - console.log(JSON.stringify(subscription)); - - // Register subscription with LSP server - const response = await fetch( - "/push-notifications/subscribe", { - method: "post", - headers: {"Content-Type": "application/json"}, - body: JSON.stringify(subscription) - } - ); - - const rspJson = await response.json(); - console.log("Subscription ID:", rspJson.subid); - window.localStorage.setItem("subid", rspJson.subid); - } + await syncPushSubscription(); } catch (err) { console.log("Error subscribing to push notifications:", err); -- 2.39.5