feat(push): smart push queueing with page-visibility fast path and app-ping/pong fallback

This commit is contained in:
2026-06-04 22:10:48 -04:00
parent fc0527e9e7
commit 4e6dfb4726
16 changed files with 192 additions and 60 deletions
+22 -5
View File
@@ -21,6 +21,7 @@ export function initDB(config: AppConfig): void {
endpoint TEXT PRIMARY KEY,
p256dh TEXT NOT NULL,
auth TEXT NOT NULL,
device_id TEXT,
created_at TEXT DEFAULT (datetime('now')),
last_used TEXT
);
@@ -80,6 +81,13 @@ export function initDB(config: AppConfig): void {
db.exec("ALTER TABLE session_reviews ADD COLUMN parent_adapter TEXT NOT NULL DEFAULT 'claude'");
}
// Migration: add device_id column to push_subscriptions if missing
const pushInfo = db.pragma('table_info(push_subscriptions)') as { name: string }[];
if (!pushInfo.some(c => c.name === 'device_id')) {
db.exec("ALTER TABLE push_subscriptions ADD COLUMN device_id TEXT DEFAULT NULL");
}
db.exec("CREATE INDEX IF NOT EXISTS idx_push_device ON push_subscriptions(device_id)");
// Migration: add end_anchor_message_id column to session_reviews if missing
if (!reviewInfo.some(c => c.name === 'end_anchor_message_id')) {
db.exec("ALTER TABLE session_reviews ADD COLUMN end_anchor_message_id TEXT DEFAULT NULL");
@@ -109,6 +117,7 @@ function getDB(): BetterSqlite3.Database {
interface PreparedStatements {
pushSubsSave: BetterSqlite3.Statement;
pushSubsRemoveByDevice: BetterSqlite3.Statement;
pushSubsRemove: BetterSqlite3.Statement;
pushSubsGetAll: BetterSqlite3.Statement;
pushSubsMarkUsed: BetterSqlite3.Statement;
@@ -139,12 +148,16 @@ function stmts(): PreparedStatements {
_stmts = {
// push_subscriptions
pushSubsSave: d.prepare(`
INSERT INTO push_subscriptions (endpoint, p256dh, auth)
VALUES (?, ?, ?)
INSERT INTO push_subscriptions (endpoint, p256dh, auth, device_id)
VALUES (?, ?, ?, ?)
ON CONFLICT(endpoint) DO UPDATE SET
p256dh = excluded.p256dh,
auth = excluded.auth
auth = excluded.auth,
device_id = excluded.device_id
`),
pushSubsRemoveByDevice: d.prepare(
`DELETE FROM push_subscriptions WHERE device_id = ? AND endpoint != ?`
),
pushSubsRemove: d.prepare(
`DELETE FROM push_subscriptions WHERE endpoint = ?`
),
@@ -243,13 +256,17 @@ export interface PushSubRow {
endpoint: string;
p256dh: string;
auth: string;
device_id: string | null;
created_at: string;
last_used: string | null;
}
export const pushSubs = {
save(endpoint: string, p256dh: string, auth: string): void {
stmts().pushSubsSave.run(endpoint, p256dh, auth);
save(endpoint: string, p256dh: string, auth: string, deviceId: string | null): void {
stmts().pushSubsSave.run(endpoint, p256dh, auth, deviceId ?? null);
if (deviceId) {
stmts().pushSubsRemoveByDevice.run(deviceId, endpoint);
}
},
remove(endpoint: string): void {