/** * Webhooks API * GET /api/transparency/webhooks - List all webhooks * POST /api/transparency/webhooks - Register a new webhook * DELETE /api/transparency/webhooks - Remove a webhook * * Manage webhook subscriptions for real-time notifications. */ import type { NextApiRequest, NextApiResponse } from 'next'; import { getEventStream, TransparencyEventType } from '../../../lib/transparency'; export default async function handler( req: NextApiRequest, res: NextApiResponse ) { const eventStream = getEventStream(); if (req.method === 'GET') { try { const webhooks = eventStream.getWebhooks(); // Mask secrets for security const safeWebhooks = webhooks.map(wh => ({ ...wh, secret: wh.secret.substring(0, 8) + '...' })); return res.status(200).json({ success: true, data: { webhooks: safeWebhooks, count: safeWebhooks.length } }); } catch (error) { console.error('[API] Webhooks list error:', error); return res.status(500).json({ success: false, error: 'Failed to list webhooks' }); } } if (req.method === 'POST') { try { const { url, types, secret } = req.body; if (!url || !types || !Array.isArray(types)) { return res.status(400).json({ success: false, error: 'Missing required fields: url, types (array)' }); } // Validate URL try { new URL(url); } catch { return res.status(400).json({ success: false, error: 'Invalid webhook URL' }); } // Validate event types const availableTypes = eventStream.getAvailableEventTypes(); const invalidTypes = types.filter((t: string) => !availableTypes.includes(t as TransparencyEventType)); if (invalidTypes.length > 0) { return res.status(400).json({ success: false, error: `Invalid event types: ${invalidTypes.join(', ')}` }); } const webhook = eventStream.registerWebhook(url, types as TransparencyEventType[], secret); return res.status(201).json({ success: true, data: { id: webhook.id, url: webhook.url, types: webhook.types, secret: webhook.secret, // Return full secret only on creation active: webhook.active } }); } catch (error) { console.error('[API] Webhook create error:', error); return res.status(500).json({ success: false, error: 'Failed to create webhook' }); } } if (req.method === 'DELETE') { try { const { id } = req.body; if (!id) { return res.status(400).json({ success: false, error: 'Missing required field: id' }); } const removed = eventStream.removeWebhook(id); if (!removed) { return res.status(404).json({ success: false, error: 'Webhook not found' }); } return res.status(200).json({ success: true, message: 'Webhook removed successfully' }); } catch (error) { console.error('[API] Webhook delete error:', error); return res.status(500).json({ success: false, error: 'Failed to delete webhook' }); } } if (req.method === 'PATCH') { try { const { id, active } = req.body; if (!id) { return res.status(400).json({ success: false, error: 'Missing required field: id' }); } const webhook = eventStream.updateWebhook(id, { active }); if (!webhook) { return res.status(404).json({ success: false, error: 'Webhook not found' }); } return res.status(200).json({ success: true, data: { ...webhook, secret: webhook.secret.substring(0, 8) + '...' } }); } catch (error) { console.error('[API] Webhook update error:', error); return res.status(500).json({ success: false, error: 'Failed to update webhook' }); } } return res.status(405).json({ error: 'Method not allowed' }); }