From bc3b12de499e40d1a2a9bdb81a583b1707973a95 Mon Sep 17 00:00:00 2001 From: Bas van den Aakster Date: Sun, 28 Sep 2025 16:43:29 +0200 Subject: [PATCH] Refactor: Simplify notifications plugin --- package.json | 2 +- src/endpoints/push-notifications.ts | 151 +--------------------------- src/exports/client.ts | 4 +- tsconfig.json | 43 ++++---- 4 files changed, 33 insertions(+), 167 deletions(-) diff --git a/package.json b/package.json index 2676e79..98a883a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xtr-dev/payload-notifications", - "version": "0.0.2", + "version": "0.0.3", "description": "A PayloadCMS plugin that adds a configurable notifications collection for sending messages with titles, content, and attachable relationship items", "license": "MIT", "type": "module", diff --git a/src/endpoints/push-notifications.ts b/src/endpoints/push-notifications.ts index 22b845a..df52f8c 100644 --- a/src/endpoints/push-notifications.ts +++ b/src/endpoints/push-notifications.ts @@ -24,11 +24,11 @@ export function createPushNotificationEndpoints(options: NotificationsPluginOpti } const body = await req.json?.() - if (!body) { + if (!body || !(typeof body === 'object' && 'subscription' in body && 'userAgent' in body && 'channels' in body)) { return Response.json({ error: 'Invalid request body' }, { status: 400 }) } - const { subscription, userAgent, channels } = body + const { subscription, userAgent, channels } = body as { subscription: any, userAgent: string, channels: string[] } if (!subscription || !subscription.endpoint) { return Response.json({ error: 'Invalid subscription data' }, { status: 400 }) @@ -60,11 +60,11 @@ export function createPushNotificationEndpoints(options: NotificationsPluginOpti handler: async (req: PayloadRequest) => { try { const body = await req.json?.() - if (!body) { + if (!body || !(typeof body === 'object' && 'endpoint' in body)) { return Response.json({ error: 'Invalid request body' }, { status: 400 }) } - const { endpoint } = body + const { endpoint } = body as { endpoint: string } if (!endpoint) { return Response.json({ error: 'Endpoint is required' }, { status: 400 }) @@ -102,148 +102,5 @@ export function createPushNotificationEndpoints(options: NotificationsPluginOpti } }, }, - - // Send test notification (admin only) - { - path: '/push-notifications/test', - method: 'post', - handler: async (req: PayloadRequest) => { - try { - if (!req.user || req.user.role !== 'admin') { - return Response.json({ error: 'Admin access required' }, { status: 403 }) - } - - const body = await req.json?.() - if (!body) { - return Response.json({ error: 'Invalid request body' }, { status: 400 }) - } - - const { userId, title, body: messageBody, options: notificationOptions } = body - - if (!userId || !title || !messageBody) { - return Response.json( - { error: 'userId, title, and body are required' }, - { status: 400 } - ) - } - - const pushManager = new WebPushManager(webPushConfig, req.payload) - const results = await pushManager.sendToUser( - userId, - title, - messageBody, - notificationOptions - ) - - return Response.json({ - success: true, - results, - sent: results.filter(r => r.success).length, - failed: results.filter(r => !r.success).length, - }) - } catch (error: any) { - console.error('Test notification error:', error) - return Response.json( - { error: 'Failed to send test notification' }, - { status: 500 } - ) - } - }, - }, - - // Send notification to user (authenticated users can send to themselves, admins to anyone) - { - path: '/push-notifications/send', - method: 'post', - handler: async (req: PayloadRequest) => { - try { - if (!req.user) { - return Response.json({ error: 'Authentication required' }, { status: 401 }) - } - - const body = await req.json?.() - if (!body) { - return Response.json({ error: 'Invalid request body' }, { status: 400 }) - } - - const { userId, title, body: messageBody, options: notificationOptions } = body - - if (!userId || !title || !messageBody) { - return Response.json( - { error: 'userId, title, and body are required' }, - { status: 400 } - ) - } - - // Users can only send notifications to themselves, admins can send to anyone - if (userId !== req.user.id && req.user.role !== 'admin') { - return Response.json( - { error: 'You can only send notifications to yourself' }, - { status: 403 } - ) - } - - const pushManager = new WebPushManager(webPushConfig, req.payload) - const results = await pushManager.sendToUser( - userId, - title, - messageBody, - notificationOptions - ) - - return Response.json({ - success: true, - results, - sent: results.filter(r => r.success).length, - failed: results.filter(r => !r.success).length, - }) - } catch (error: any) { - console.error('Send notification error:', error) - return Response.json( - { error: 'Failed to send notification' }, - { status: 500 } - ) - } - }, - }, - - // Tracking endpoint for analytics - { - path: '/push-notifications/track', - method: 'post', - handler: async (req: PayloadRequest) => { - try { - const body = await req.json?.() - if (!body) { - return Response.json({ error: 'Invalid request body' }, { status: 400 }) - } - - const { action, notificationId, timestamp } = body - - // Log the tracking event (you can extend this to save to database) - console.log('Push notification tracking:', { - action, - notificationId, - timestamp, - userAgent: req.headers.get('user-agent'), - // Note: req.ip may not be available in all environments - }) - - // You could save tracking data to a collection here - // await req.payload.create({ - // collection: 'notification-analytics', - // data: { action, notificationId, timestamp, ... } - // }) - - return Response.json({ success: true }) - } catch (error: any) { - console.error('Tracking error:', error) - return Response.json( - { error: 'Failed to track notification event' }, - { status: 500 } - ) - } - }, - }, ] } diff --git a/src/exports/client.ts b/src/exports/client.ts index 2bf609c..3741b36 100644 --- a/src/exports/client.ts +++ b/src/exports/client.ts @@ -143,7 +143,7 @@ export function usePushNotifications(vapidPublicKey: string) { const [isSupported, setIsSupported] = ReactHooks.useState(false) const [isSubscribed, setIsSubscribed] = ReactHooks.useState(false) - const [permission, setPermission] = ReactHooks.useState('default' as NotificationPermission) + const [permission, setPermission] = ReactHooks.useState('default') const [pushManager, setPushManager] = ReactHooks.useState(null) ReactHooks.useEffect(() => { @@ -187,4 +187,4 @@ export function usePushNotifications(vapidPublicKey: string) { requestPermission, pushManager, } -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index 67e1b07..f7d892d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,26 +1,35 @@ { "compilerOptions": { - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "lib": ["ES2022", "DOM", "WebWorker"], - "module": "ESNext", - "moduleResolution": "Bundler", - "noEmit": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true, + "baseUrl": ".", "target": "ES2022", + "module": "ES2022", + "moduleResolution": "node", + "lib": [ + "ES2022", + "DOM", + "DOM.Iterable", + "WebWorker" + ], + "outDir": "./dist", + "rootDir": "./src", "declaration": true, "declarationMap": true, - "sourceMap": true, - "outDir": "./dist", - "rootDir": "./src" + "sourceMap": false, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true }, "include": [ - "./src/**/*.ts", - "./src/**/*.tsx", - "./dev/next-env.d.ts" + "src/**/*" ], - "exclude": ["node_modules", "dist"] + "exclude": [ + "src/**/*.test.ts", + "src/**/*.spec.ts", + "dev", + "node_modules", + "dist" + ] }