localgreenchain/pages/api/transparency/webhooks.ts
Claude 0fcc2763fe
Add comprehensive transparency system for LocalGreenChain
This commit introduces a complete transparency infrastructure including:

Core Transparency Modules:
- AuditLog: Immutable, cryptographically-linked audit trail for all actions
- EventStream: Real-time SSE streaming and webhook support
- TransparencyDashboard: Aggregated metrics and system health monitoring
- DigitalSignatures: Cryptographic verification for handoffs and certificates

API Endpoints:
- /api/transparency/dashboard - Full platform metrics
- /api/transparency/audit - Query and log audit entries
- /api/transparency/events - SSE stream and event history
- /api/transparency/webhooks - Webhook management
- /api/transparency/signatures - Digital signature operations
- /api/transparency/certificate/[plantId] - Plant authenticity certificates
- /api/transparency/export - Multi-format data export
- /api/transparency/report - Compliance reporting
- /api/transparency/health - System health checks

Features:
- Immutable audit logging with chain integrity verification
- Real-time event streaming via Server-Sent Events
- Webhook support with HMAC signature verification
- Digital signatures for transport handoffs and ownership transfers
- Certificate of Authenticity generation for plants
- Multi-format data export (JSON, CSV, summary)
- Public transparency portal at /transparency
- System health monitoring for all components

Documentation:
- Comprehensive TRANSPARENCY.md guide with API examples
2025-11-23 03:29:56 +00:00

167 lines
4.2 KiB
TypeScript

/**
* 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' });
}