mirror of
https://github.com/xtr-dev/payload-notifications.git
synced 2025-12-09 18:33:24 +00:00
Refactor: Simplify notifications plugin
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@xtr-dev/payload-notifications",
|
"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",
|
"description": "A PayloadCMS plugin that adds a configurable notifications collection for sending messages with titles, content, and attachable relationship items",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ export function createPushNotificationEndpoints(options: NotificationsPluginOpti
|
|||||||
}
|
}
|
||||||
|
|
||||||
const body = await req.json?.()
|
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 })
|
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) {
|
if (!subscription || !subscription.endpoint) {
|
||||||
return Response.json({ error: 'Invalid subscription data' }, { status: 400 })
|
return Response.json({ error: 'Invalid subscription data' }, { status: 400 })
|
||||||
@@ -60,11 +60,11 @@ export function createPushNotificationEndpoints(options: NotificationsPluginOpti
|
|||||||
handler: async (req: PayloadRequest) => {
|
handler: async (req: PayloadRequest) => {
|
||||||
try {
|
try {
|
||||||
const body = await req.json?.()
|
const body = await req.json?.()
|
||||||
if (!body) {
|
if (!body || !(typeof body === 'object' && 'endpoint' in body)) {
|
||||||
return Response.json({ error: 'Invalid request body' }, { status: 400 })
|
return Response.json({ error: 'Invalid request body' }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { endpoint } = body
|
const { endpoint } = body as { endpoint: string }
|
||||||
|
|
||||||
if (!endpoint) {
|
if (!endpoint) {
|
||||||
return Response.json({ error: 'Endpoint is required' }, { status: 400 })
|
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 }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ export function usePushNotifications(vapidPublicKey: string) {
|
|||||||
|
|
||||||
const [isSupported, setIsSupported] = ReactHooks.useState(false)
|
const [isSupported, setIsSupported] = ReactHooks.useState(false)
|
||||||
const [isSubscribed, setIsSubscribed] = 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)
|
const [pushManager, setPushManager] = ReactHooks.useState(null)
|
||||||
|
|
||||||
ReactHooks.useEffect(() => {
|
ReactHooks.useEffect(() => {
|
||||||
@@ -187,4 +187,4 @@ export function usePushNotifications(vapidPublicKey: string) {
|
|||||||
requestPermission,
|
requestPermission,
|
||||||
pushManager,
|
pushManager,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,35 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowSyntheticDefaultImports": true,
|
"baseUrl": ".",
|
||||||
"esModuleInterop": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"lib": ["ES2022", "DOM", "WebWorker"],
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "Bundler",
|
|
||||||
"noEmit": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"strict": true,
|
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
|
"module": "ES2022",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"lib": [
|
||||||
|
"ES2022",
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable",
|
||||||
|
"WebWorker"
|
||||||
|
],
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"declarationMap": true,
|
"declarationMap": true,
|
||||||
"sourceMap": true,
|
"sourceMap": false,
|
||||||
"outDir": "./dist",
|
"strict": true,
|
||||||
"rootDir": "./src"
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./src/**/*.ts",
|
"src/**/*"
|
||||||
"./src/**/*.tsx",
|
|
||||||
"./dev/next-env.d.ts"
|
|
||||||
],
|
],
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": [
|
||||||
|
"src/**/*.test.ts",
|
||||||
|
"src/**/*.spec.ts",
|
||||||
|
"dev",
|
||||||
|
"node_modules",
|
||||||
|
"dist"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user