import { NextRequest, NextResponse } from "next/server"; import { supabase } from "@/lib/supabase"; const webpush = require("web-push"); export const dynamic = "force-dynamic"; export const runtime = "nodejs"; // Set VAPID details (should be configured in environment) if (process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY && process.env.VAPID_PRIVATE_KEY) { webpush.setVapidDetails( process.env.VAPID_SUBJECT || "mailto:noreply@buildmate.com", process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY, process.env.VAPID_PRIVATE_KEY ); } export async function POST(request: NextRequest) { try { const { userId, notificationType, title, body, url, icon, badge } = await request.json(); if (!userId || !notificationType || !title || !body) { return NextResponse.json( { error: "Missing required fields: userId, notificationType, title, body" }, { status: 400 } ); } // Load preferences row from dedicated table const { data: prefRow, error: userError } = await supabase .from("notification_preferences") .select("*") .eq("user_id", userId) .single(); if (userError) { console.error("Error fetching notification preferences:", userError); return NextResponse.json({ error: "Preferences not found" }, { status: 404 }); } // Convert row into unified prefs object let prefs: Record = {}; if (prefRow) { prefs = { buildLikes: prefRow.build_likes, comments: prefRow.comments, followers: prefRow.followers, newsletter: prefRow.newsletter, emailUpdates: prefRow.email_updates, }; } // Map notification types to preference keys const typeToPreferenceKey: { [key: string]: string } = { buildLike: "buildLikes", buildLikes: "buildLikes", comment: "comments", comments: "comments", follower: "followers", followers: "followers", }; const prefKey = typeToPreferenceKey[notificationType]; // Check if user has enabled this notification type if (prefKey && prefs[prefKey] === false) { return NextResponse.json({ success: true, message: `User has disabled ${notificationType} notifications` }); } // Get all push subscriptions for this user const { data: subscriptions, error } = await supabase .from("push_subscriptions") .select("*") .eq("user_id", userId); if (error) { console.error("Error fetching subscriptions:", error); return NextResponse.json({ error: "Failed to fetch subscriptions" }, { status: 500 }); } if (!subscriptions || subscriptions.length === 0) { return NextResponse.json({ success: true, message: "No active subscriptions" }); } const payload = { title, body, icon: icon || "/buildmate-icon.svg", badge: badge || "/buildmate-icon.svg", tag: `${notificationType}-notification`, data: { url: url || "/dashboard", timestamp: new Date().toISOString(), }, }; const failedSubscriptions = []; const successCount = subscriptions.length; // Send push notification to all subscriptions for (const subscription of subscriptions) { try { const pushSubscription = { endpoint: subscription.endpoint, keys: { auth: subscription.auth_key, p256dh: subscription.p256dh_key, }, }; await webpush.sendNotification(pushSubscription, JSON.stringify(payload)); } catch (pushError: any) { console.error(`Failed to send push to ${subscription.endpoint}:`, pushError); // If subscription is invalid (410 Gone), remove it from database if (pushError.statusCode === 410) { await supabase .from("push_subscriptions") .delete() .eq("id", subscription.id); } failedSubscriptions.push(subscription.id); } } return NextResponse.json({ success: true, sent: successCount - failedSubscriptions.length, failed: failedSubscriptions.length, }); } catch (error: any) { console.error("Send push notification error:", error); return NextResponse.json({ error: error.message || "Internal server error" }, { status: 500 }); } }