chore: add debug logging for push notifications
This commit is contained in:
@@ -1,35 +1,49 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useState, useEffect } from "react"
|
import { useState, useEffect } from "react"
|
||||||
import { Bell, BellOff, Loader2 } from "lucide-react"
|
import { Bell, BellOff, Loader2, AlertTriangle } from "lucide-react"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
import { subscribeUser, unsubscribeUser } from "@/app/actions/subscription"
|
import { subscribeUser, unsubscribeUser } from "@/app/actions/subscription"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
|
|
||||||
function urlBase64ToUint8Array(base64String: string) {
|
function urlBase64ToUint8Array(base64String: string) {
|
||||||
const padding = '='.repeat((4 - base64String.length % 4) % 4)
|
try {
|
||||||
const base64 = (base64String + padding)
|
const padding = '='.repeat((4 - base64String.length % 4) % 4)
|
||||||
.replace(/\-/g, '+')
|
const base64 = (base64String + padding)
|
||||||
.replace(/_/g, '/')
|
.replace(/\-/g, '+')
|
||||||
|
.replace(/_/g, '/')
|
||||||
|
|
||||||
const rawData = window.atob(base64)
|
const rawData = window.atob(base64)
|
||||||
const outputArray = new Uint8Array(rawData.length)
|
const outputArray = new Uint8Array(rawData.length)
|
||||||
|
|
||||||
for (let i = 0; i < rawData.length; ++i) {
|
for (let i = 0; i < rawData.length; ++i) {
|
||||||
outputArray[i] = rawData.charCodeAt(i)
|
outputArray[i] = rawData.charCodeAt(i)
|
||||||
|
}
|
||||||
|
return outputArray
|
||||||
|
} catch (e) {
|
||||||
|
console.error("VAPID Key conversion failed", e)
|
||||||
|
throw new Error("Invalid VAPID Key format")
|
||||||
}
|
}
|
||||||
return outputArray
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PushSubscriptionSettings({ planId }: { planId: string }) {
|
export function PushSubscriptionSettings({ planId }: { planId: string }) {
|
||||||
const [isSupported, setIsSupported] = useState(false)
|
const [isSupported, setIsSupported] = useState(false)
|
||||||
const [subscription, setSubscription] = useState<PushSubscription | null>(null)
|
const [subscription, setSubscription] = useState<PushSubscription | null>(null)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [debugInfo, setDebugInfo] = useState<string | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if ('serviceWorker' in navigator && 'PushManager' in window && process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY) {
|
const checks = []
|
||||||
|
if (!('serviceWorker' in navigator)) checks.push("No Service Worker support")
|
||||||
|
if (!('PushManager' in window)) checks.push("No PushManager support")
|
||||||
|
if (!process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY) checks.push("Missing VAPID Key")
|
||||||
|
|
||||||
|
if (checks.length === 0) {
|
||||||
setIsSupported(true)
|
setIsSupported(true)
|
||||||
registerServiceWorker()
|
registerServiceWorker()
|
||||||
|
} else {
|
||||||
|
console.warn("Push not supported:", checks.join(", "))
|
||||||
|
setDebugInfo(checks.join(", "))
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@@ -38,16 +52,19 @@ export function PushSubscriptionSettings({ planId }: { planId: string }) {
|
|||||||
const registration = await navigator.serviceWorker.ready
|
const registration = await navigator.serviceWorker.ready
|
||||||
const sub = await registration.pushManager.getSubscription()
|
const sub = await registration.pushManager.getSubscription()
|
||||||
setSubscription(sub)
|
setSubscription(sub)
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
console.error("SW registration error", e)
|
console.error("SW registration error", e)
|
||||||
|
setDebugInfo(`SW Error: ${e.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function subscribe() {
|
async function subscribe() {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
setDebugInfo(null)
|
||||||
try {
|
try {
|
||||||
// Explicitly request permission first
|
console.log("Requesting permission...")
|
||||||
const permission = await Notification.requestPermission()
|
const permission = await Notification.requestPermission()
|
||||||
|
console.log("Permission result:", permission)
|
||||||
|
|
||||||
if (permission === 'denied') {
|
if (permission === 'denied') {
|
||||||
toast.error("Notifications are blocked in your browser settings.")
|
toast.error("Notifications are blocked in your browser settings.")
|
||||||
@@ -61,17 +78,23 @@ export function PushSubscriptionSettings({ planId }: { planId: string }) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Waiting for SW ready...")
|
||||||
const registration = await navigator.serviceWorker.ready
|
const registration = await navigator.serviceWorker.ready
|
||||||
|
console.log("SW Ready. Subscribing...")
|
||||||
|
|
||||||
const sub = await registration.pushManager.subscribe({
|
const sub = await registration.pushManager.subscribe({
|
||||||
userVisibleOnly: true,
|
userVisibleOnly: true,
|
||||||
applicationServerKey: urlBase64ToUint8Array(process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!)
|
applicationServerKey: urlBase64ToUint8Array(process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log("Subscribed locally:", sub)
|
||||||
setSubscription(sub)
|
setSubscription(sub)
|
||||||
await subscribeUser(planId, sub.toJSON())
|
await subscribeUser(planId, sub.toJSON())
|
||||||
toast.success("Push Notifications enabled")
|
toast.success("Push Notifications enabled")
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error("Subscription failed", error)
|
console.error("Subscription failed", error)
|
||||||
toast.error("Failed to enable notifications. Please check site permissions.")
|
setDebugInfo(`Error: ${error.message || "Unknown error"}`)
|
||||||
|
toast.error("Failed to enable notifications. See debug info.")
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
@@ -94,26 +117,34 @@ export function PushSubscriptionSettings({ planId }: { planId: string }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isSupported) return null
|
if (!isSupported && !debugInfo) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between p-4 border rounded-md mb-4 bg-muted/50">
|
<div className="flex flex-col gap-2 p-4 border rounded-md mb-4 bg-muted/50">
|
||||||
<div className="space-y-0.5">
|
<div className="flex items-center justify-between">
|
||||||
<h3 className="font-medium text-sm">Device Notifications</h3>
|
<div className="space-y-0.5">
|
||||||
<p className="text-xs text-muted-foreground">
|
<h3 className="font-medium text-sm">Device Notifications</h3>
|
||||||
Receive updates on this device
|
<p className="text-xs text-muted-foreground">
|
||||||
</p>
|
Receive updates on this device
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{subscription ? (
|
||||||
|
<Button variant="outline" size="sm" onClick={unsubscribe} disabled={loading}>
|
||||||
|
{loading ? <Loader2 className="w-4 h-4 animate-spin" /> : <BellOff className="w-4 h-4 mr-2" />}
|
||||||
|
Disable
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button size="sm" onClick={subscribe} disabled={loading || !isSupported} variant="secondary">
|
||||||
|
{loading ? <Loader2 className="w-4 h-4 animate-spin" /> : <Bell className="w-4 h-4 mr-2" />}
|
||||||
|
Enable
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{subscription ? (
|
{debugInfo && (
|
||||||
<Button variant="outline" size="sm" onClick={unsubscribe} disabled={loading}>
|
<div className="text-xs text-destructive flex items-center gap-1 bg-destructive/10 p-2 rounded">
|
||||||
{loading ? <Loader2 className="w-4 h-4 animate-spin" /> : <BellOff className="w-4 h-4 mr-2" />}
|
<AlertTriangle className="w-3 h-3" />
|
||||||
Disable
|
{debugInfo}
|
||||||
</Button>
|
</div>
|
||||||
) : (
|
|
||||||
<Button size="sm" onClick={subscribe} disabled={loading} variant="secondary">
|
|
||||||
{loading ? <Loader2 className="w-4 h-4 animate-spin" /> : <Bell className="w-4 h-4 mr-2" />}
|
|
||||||
Enable
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export async function sendPlanNotification(planId: string, message: string, webh
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (subscriptions.length > 0) {
|
if (subscriptions.length > 0) {
|
||||||
|
console.log(`[Push] Found ${subscriptions.length} subscriptions for plan ${planId}`);
|
||||||
const payload = {
|
const payload = {
|
||||||
title: "Cat Sitting Planner",
|
title: "Cat Sitting Planner",
|
||||||
body: message,
|
body: message,
|
||||||
@@ -56,12 +57,24 @@ export async function sendPlanNotification(planId: string, message: string, webh
|
|||||||
|
|
||||||
subscriptions.forEach(sub => {
|
subscriptions.forEach(sub => {
|
||||||
promises.push((async () => {
|
promises.push((async () => {
|
||||||
const res = await sendPushNotification(sub, payload);
|
try {
|
||||||
if (!res.success && (res.statusCode === 410 || res.statusCode === 404)) {
|
console.log(`[Push] Sending to endpoint ${sub.endpoint.slice(0, 20)}...`);
|
||||||
await prisma.pushSubscription.delete({ where: { id: sub.id } });
|
const res = await sendPushNotification(sub, payload);
|
||||||
|
console.log(`[Push] Result for ${sub.endpoint.slice(0, 20)}...:`, res);
|
||||||
|
if (!res.success) {
|
||||||
|
console.error(`[Push] Failed for endpoint: ${res.statusCode}`);
|
||||||
|
if (res.statusCode === 410 || res.statusCode === 404) {
|
||||||
|
console.log(`[Push] Deleting stale subscription`);
|
||||||
|
await prisma.pushSubscription.delete({ where: { id: sub.id } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[Push] Error inside loop:`, err);
|
||||||
}
|
}
|
||||||
})());
|
})());
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
console.log(`[Push] No subscriptions found for plan ${planId}`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to fetch/send push subscriptions", e);
|
console.error("Failed to fetch/send push subscriptions", e);
|
||||||
|
|||||||
@@ -18,13 +18,15 @@ interface PushSubscriptionData {
|
|||||||
|
|
||||||
export async function sendPushNotification(subscription: PushSubscriptionData, payload: any) {
|
export async function sendPushNotification(subscription: PushSubscriptionData, payload: any) {
|
||||||
try {
|
try {
|
||||||
await webpush.sendNotification({
|
console.log(`[PushLib] Sending to ${subscription.endpoint.slice(0, 30)}...`);
|
||||||
|
const result = await webpush.sendNotification({
|
||||||
endpoint: subscription.endpoint,
|
endpoint: subscription.endpoint,
|
||||||
keys: {
|
keys: {
|
||||||
p256dh: subscription.p256dh,
|
p256dh: subscription.p256dh,
|
||||||
auth: subscription.auth,
|
auth: subscription.auth,
|
||||||
}
|
}
|
||||||
}, JSON.stringify(payload));
|
}, JSON.stringify(payload));
|
||||||
|
console.log(`[PushLib] Success: ${result.statusCode}`);
|
||||||
return { success: true, statusCode: 201 };
|
return { success: true, statusCode: 201 };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.statusCode === 410 || error.statusCode === 404) {
|
if (error.statusCode === 410 || error.statusCode === 404) {
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
self.addEventListener('push', function (event) {
|
self.addEventListener('push', function (event) {
|
||||||
if (!event.data) return;
|
console.log('[SW] Push Received', event);
|
||||||
|
if (!event.data) {
|
||||||
|
console.log('[SW] No data provided in push event');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = event.data.json();
|
const data = event.data.json();
|
||||||
|
console.log('[SW] Push Data:', data);
|
||||||
const title = data.title || 'Cat Sitting Planner';
|
const title = data.title || 'Cat Sitting Planner';
|
||||||
const options = {
|
const options = {
|
||||||
body: data.body || '',
|
body: data.body || '',
|
||||||
@@ -15,6 +20,8 @@ self.addEventListener('push', function (event) {
|
|||||||
|
|
||||||
event.waitUntil(
|
event.waitUntil(
|
||||||
self.registration.showNotification(title, options)
|
self.registration.showNotification(title, options)
|
||||||
|
.then(() => console.log('[SW] Notification shown'))
|
||||||
|
.catch(e => console.error('[SW] Error showing notification:', e))
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error processing push event:', err);
|
console.error('Error processing push event:', err);
|
||||||
|
|||||||
Reference in New Issue
Block a user