localgreenchain/pages/api/notifications/subscribe.ts
Claude 62c1ded598
Add comprehensive notification system (Agent 8)
Implement multi-channel notification system with:
- Core notification service with email, push, and in-app channels
- Email templates for all notification types (welcome, plant registered,
  transport alerts, farm alerts, harvest ready, demand matches, weekly digest)
- Push notification support with VAPID authentication
- In-app notification management with read/unread tracking
- Notification scheduler for recurring and scheduled notifications
- API endpoints for notifications CRUD, preferences, and subscriptions
- UI components (NotificationBell, NotificationList, NotificationItem,
  PreferencesForm)
- Full notifications page with preferences management
- Service worker for push notification handling
2025-11-23 03:52:41 +00:00

98 lines
2.6 KiB
TypeScript

/**
* API: Push Notification Subscription Endpoint
* POST /api/notifications/subscribe - Subscribe to push notifications
* DELETE /api/notifications/subscribe - Unsubscribe from push notifications
* GET /api/notifications/subscribe - Get VAPID public key
*/
import type { NextApiRequest, NextApiResponse } from 'next';
import { PushChannel } from '../../../lib/notifications/channels/push';
// Create push channel instance for subscription management
const pushChannel = new PushChannel({
vapidPublicKey: process.env.VAPID_PUBLIC_KEY || '',
vapidPrivateKey: process.env.VAPID_PRIVATE_KEY || '',
vapidSubject: process.env.VAPID_SUBJECT || 'mailto:admin@localgreenchain.local'
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// In production, get userId from session/auth
const userId = req.query.userId as string || req.body?.userId || 'demo-user';
if (req.method === 'GET') {
try {
// Return VAPID public key for client subscription
const publicKey = pushChannel.getPublicKey();
const hasSubscription = pushChannel.hasSubscription(userId);
return res.status(200).json({
success: true,
data: {
publicKey,
subscribed: hasSubscription
}
});
} catch (error: any) {
return res.status(500).json({
success: false,
error: error.message
});
}
}
if (req.method === 'POST') {
try {
const { endpoint, keys } = req.body;
if (!endpoint || !keys?.p256dh || !keys?.auth) {
return res.status(400).json({
success: false,
error: 'Invalid subscription data. Required: endpoint, keys.p256dh, keys.auth'
});
}
const subscription = pushChannel.subscribe(userId, { endpoint, keys });
return res.status(201).json({
success: true,
data: {
subscribed: true,
subscription: {
endpoint: subscription.endpoint,
createdAt: subscription.createdAt
}
}
});
} catch (error: any) {
return res.status(500).json({
success: false,
error: error.message
});
}
}
if (req.method === 'DELETE') {
try {
const { endpoint } = req.body;
const success = pushChannel.unsubscribe(userId, endpoint);
return res.status(200).json({
success: true,
data: {
unsubscribed: success
}
});
} catch (error: any) {
return res.status(500).json({
success: false,
error: error.message
});
}
}
return res.status(405).json({ error: 'Method not allowed' });
}