Agent query: Could you check the browser's console logs for any subscription errors when clicking the subscribe button?

Fix: Implement robust push notification subscription handling, including error handling and welcome notification.  Add necessary type definitions and VAPID key validation.

Screenshot: https://storage.googleapis.com/screenshot-production-us-central1/9dda30b6-4149-4bce-89dc-76333005952c/2539846f-42c7-4fad-9462-984f310cf386.jpg
This commit is contained in:
TerribleDev
2025-02-15 18:10:12 +00:00
parent 82299a45bf
commit 615579e317
4 changed files with 84 additions and 11 deletions

View File

@@ -89,18 +89,27 @@ export default function Home() {
throw new Error('Notification permission denied');
}
console.log('Getting service worker registration...');
const registration = await navigator.serviceWorker.ready;
console.log('Service worker registered successfully');
const vapidPublicKey = import.meta.env.VITE_VAPID_PUBLIC_KEY;
if (!vapidPublicKey) {
throw new Error('VAPID public key is not configured');
}
console.log('VAPID public key available:', vapidPublicKey.slice(0, 10) + '...');
const convertedVapidKey = urlBase64ToUint8Array(vapidPublicKey);
console.log('Converted VAPID key to Uint8Array');
console.log('Requesting push subscription...');
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: convertedVapidKey
});
console.log('Successfully subscribed to push notifications');
console.log('Sending subscription to server...');
await apiRequest('POST', '/api/subscriptions', subscription);
setIsSubscribed(true);
toast({
@@ -108,6 +117,7 @@ export default function Home() {
description: "You'll receive notifications for new newsletters",
});
} catch (error: any) {
console.error('Subscription error:', error);
toast({
title: "Error",
description: error.message || "Failed to subscribe to notifications",

20
package-lock.json generated
View File

@@ -41,6 +41,8 @@
"@radix-ui/react-tooltip": "^1.1.3",
"@replit/vite-plugin-shadcn-theme-json": "^0.0.4",
"@tanstack/react-query": "^5.60.5",
"@types/node-schedule": "^2.1.7",
"@types/web-push": "^3.6.4",
"axios": "^1.7.9",
"cheerio": "^1.0.0",
"class-variance-authority": "^0.7.0",
@@ -3361,6 +3363,15 @@
"undici-types": "~6.19.2"
}
},
"node_modules/@types/node-schedule": {
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/@types/node-schedule/-/node-schedule-2.1.7.tgz",
"integrity": "sha512-G7Z3R9H7r3TowoH6D2pkzUHPhcJrDF4Jz1JOQ80AX0K2DWTHoN9VC94XzFAPNMdbW9TBzMZ3LjpFi7RYdbxtXA==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/passport": {
"version": "1.0.17",
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz",
@@ -3470,6 +3481,15 @@
"@types/send": "*"
}
},
"node_modules/@types/web-push": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/@types/web-push/-/web-push-3.6.4.tgz",
"integrity": "sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/ws": {
"version": "8.5.13",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",

View File

@@ -43,6 +43,8 @@
"@radix-ui/react-tooltip": "^1.1.3",
"@replit/vite-plugin-shadcn-theme-json": "^0.0.4",
"@tanstack/react-query": "^5.60.5",
"@types/node-schedule": "^2.1.7",
"@types/web-push": "^3.6.4",
"axios": "^1.7.9",
"cheerio": "^1.0.0",
"class-variance-authority": "^0.7.0",

View File

@@ -7,14 +7,17 @@ import webpush from "web-push";
import schedule from "node-schedule";
// Initialize web-push with VAPID keys
if (!process.env.VAPID_PUBLIC_KEY || !process.env.VAPID_PRIVATE_KEY) {
console.warn('VAPID keys not set. Push notifications will not work.');
const vapidPublicKey = process.env.VAPID_PUBLIC_KEY;
const vapidPrivateKey = process.env.VAPID_PRIVATE_KEY;
if (!vapidPublicKey || !vapidPrivateKey) {
throw new Error('VAPID keys are required for push notifications. Please set VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY environment variables.');
}
webpush.setVapidDetails(
'mailto:team@downtowner.com',
process.env.VAPID_PUBLIC_KEY || '',
process.env.VAPID_PRIVATE_KEY || ''
vapidPublicKey,
vapidPrivateKey
);
export async function registerRoutes(app: Express): Promise<Server> {
@@ -32,16 +35,19 @@ export async function registerRoutes(app: Express): Promise<Server> {
if (newNewsletters.length > 0) {
await storage.importNewsletters(newNewsletters);
console.log(`Found ${newNewsletters.length} new newsletters, sending notifications...`);
// Send push notifications
const subscriptions = await storage.getSubscriptions();
console.log(`Sending notifications to ${subscriptions.length} subscribers`);
const notificationPayload = JSON.stringify({
title: 'New Newsletters Available',
body: `${newNewsletters.length} new newsletter${newNewsletters.length > 1 ? 's' : ''} published!`,
icon: '/icon.png'
});
await Promise.allSettled(
const results = await Promise.allSettled(
subscriptions.map(subscription =>
webpush.sendNotification({
endpoint: subscription.endpoint,
@@ -52,6 +58,10 @@ export async function registerRoutes(app: Express): Promise<Server> {
}, notificationPayload)
)
);
const succeeded = results.filter(r => r.status === 'fulfilled').length;
const failed = results.filter(r => r.status === 'rejected').length;
console.log(`Push notifications sent: ${succeeded} succeeded, ${failed} failed`);
}
} catch (error) {
console.error('Background job failed:', error);
@@ -83,16 +93,47 @@ export async function registerRoutes(app: Express): Promise<Server> {
app.post("/api/subscriptions", async (req, res) => {
try {
const subscription = req.body;
await storage.addSubscription({
endpoint: subscription.endpoint,
auth: subscription.keys.auth,
p256dh: subscription.keys.p256dh
console.log('Received subscription request:', {
endpoint: req.body.endpoint,
auth: req.body.keys?.auth ? '[present]' : '[missing]',
p256dh: req.body.keys?.p256dh ? '[present]' : '[missing]'
});
if (!req.body.endpoint || !req.body.keys?.auth || !req.body.keys?.p256dh) {
throw new Error('Invalid subscription data');
}
await storage.addSubscription({
endpoint: req.body.endpoint,
auth: req.body.keys.auth,
p256dh: req.body.keys.p256dh
});
// Test the subscription with a welcome notification
try {
await webpush.sendNotification({
endpoint: req.body.endpoint,
keys: {
auth: req.body.keys.auth,
p256dh: req.body.keys.p256dh
}
}, JSON.stringify({
title: 'Subscription Successful',
body: 'You will now receive notifications for new newsletters!',
icon: '/icon.png'
}));
console.log('Welcome notification sent successfully');
} catch (notifError) {
console.error('Failed to send welcome notification:', notifError);
}
res.json({ message: "Subscription added successfully" });
} catch (error) {
console.error('Error adding subscription:', error);
res.status(500).json({ message: "Failed to add subscription" });
res.status(500).json({
message: "Failed to add subscription",
error: error instanceof Error ? error.message : 'Unknown error'
});
}
});