localgreenchain/pages/api/transparency/export.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

313 lines
8.6 KiB
TypeScript

/**
* Data Export API
* GET /api/transparency/export - Export transparency data in various formats
*
* Provides data export capabilities for users and compliance.
*/
import type { NextApiRequest, NextApiResponse } from 'next';
import { getTransparencyDashboard, getAuditLog, getEventStream, getSignatureManager } from '../../../lib/transparency';
import * as fs from 'fs';
import * as path from 'path';
type ExportType = 'dashboard' | 'audit' | 'events' | 'plants' | 'transport' | 'signatures' | 'full';
type ExportFormat = 'json' | 'csv' | 'summary';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'GET') {
return res.status(405).json({ error: 'Method not allowed' });
}
const {
type = 'dashboard',
format = 'json',
startDate,
endDate,
plantId,
userId
} = req.query;
try {
const exportType = type as ExportType;
const exportFormat = format as ExportFormat;
let data: any;
let filename: string;
switch (exportType) {
case 'dashboard':
data = await exportDashboard(exportFormat);
filename = 'transparency-dashboard';
break;
case 'audit':
data = await exportAudit(exportFormat, startDate as string, endDate as string);
filename = 'audit-log';
break;
case 'events':
data = await exportEvents(exportFormat, startDate as string, endDate as string);
filename = 'event-stream';
break;
case 'plants':
data = await exportPlants(exportFormat, plantId as string);
filename = plantId ? `plant-${plantId}` : 'plants';
break;
case 'transport':
data = await exportTransport(exportFormat, plantId as string);
filename = 'transport-history';
break;
case 'signatures':
data = await exportSignatures(exportFormat);
filename = 'signatures';
break;
case 'full':
data = await exportFull(exportFormat);
filename = 'full-transparency-export';
break;
default:
return res.status(400).json({
success: false,
error: `Invalid export type: ${type}. Supported: dashboard, audit, events, plants, transport, signatures, full`
});
}
// Set appropriate headers based on format
const timestamp = new Date().toISOString().split('T')[0];
if (exportFormat === 'csv') {
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', `attachment; filename=${filename}-${timestamp}.csv`);
return res.status(200).send(data);
}
if (exportFormat === 'summary') {
res.setHeader('Content-Type', 'text/plain');
res.setHeader('Content-Disposition', `attachment; filename=${filename}-${timestamp}.txt`);
return res.status(200).send(data);
}
// JSON format
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Disposition', `attachment; filename=${filename}-${timestamp}.json`);
return res.status(200).json({
success: true,
exportedAt: new Date().toISOString(),
type: exportType,
data
});
} catch (error) {
console.error('[API] Export error:', error);
return res.status(500).json({
success: false,
error: 'Failed to export data'
});
}
}
async function exportDashboard(format: ExportFormat): Promise<any> {
const dashboard = getTransparencyDashboard();
return await dashboard.exportData(format);
}
async function exportAudit(format: ExportFormat, startDate?: string, endDate?: string): Promise<any> {
const auditLog = getAuditLog();
return auditLog.export(format, { startDate, endDate, limit: 10000 });
}
async function exportEvents(format: ExportFormat, startDate?: string, endDate?: string): Promise<any> {
const eventStream = getEventStream();
const events = startDate && endDate
? eventStream.getByTimeRange(startDate, endDate, { limit: 10000 })
: eventStream.getRecent(10000);
if (format === 'csv') {
const headers = ['ID', 'Type', 'Priority', 'Timestamp', 'Source', 'Data'];
const rows = events.map(e => [
e.id,
e.type,
e.priority,
e.timestamp,
e.source,
JSON.stringify(e.data).replace(/,/g, ';')
]);
return [headers.join(','), ...rows.map(r => r.join(','))].join('\n');
}
if (format === 'summary') {
const stats = eventStream.getStats();
return `
EVENT STREAM EXPORT
===================
Exported: ${new Date().toISOString()}
Total Events: ${stats.totalEvents}
Events Last 24h: ${stats.eventsLast24h}
Active Subscriptions: ${stats.activeSubscriptions}
Active Webhooks: ${stats.activeWebhooks}
Events by Type:
${Object.entries(stats.eventsByType).map(([t, c]) => ` ${t}: ${c}`).join('\n')}
Events by Priority:
${Object.entries(stats.eventsByPriority).map(([p, c]) => ` ${p}: ${c}`).join('\n')}
`.trim();
}
return events;
}
async function exportPlants(format: ExportFormat, plantId?: string): Promise<any> {
const dataDir = path.join(process.cwd(), 'data');
const chainFile = path.join(dataDir, 'plantchain.json');
if (!fs.existsSync(chainFile)) {
return format === 'json' ? [] : 'No plant data available';
}
const chainData = JSON.parse(fs.readFileSync(chainFile, 'utf-8'));
let plants = chainData.chain
?.filter((b: any) => b.plant)
.map((b: any) => ({
...b.plant,
blockIndex: b.index,
blockHash: b.hash,
timestamp: b.timestamp
})) || [];
if (plantId) {
plants = plants.filter((p: any) => p.id === plantId);
}
if (format === 'csv') {
if (plants.length === 0) return 'ID,Name,Variety,Owner,Location,Generation,Timestamp\n';
const headers = ['ID', 'Name', 'Variety', 'Owner', 'Location', 'Generation', 'Timestamp'];
const rows = plants.map((p: any) => [
p.id,
p.name || '',
p.variety || '',
p.ownerId || '',
p.location?.region || '',
p.generation || 1,
p.timestamp
]);
return [headers.join(','), ...rows.map((r: any) => r.join(','))].join('\n');
}
if (format === 'summary') {
return `
PLANT EXPORT
============
Total Plants: ${plants.length}
${plantId ? `Filtered by Plant ID: ${plantId}` : ''}
Sample Data:
${plants.slice(0, 5).map((p: any) =>
` - ${p.name || 'Unknown'} (${p.variety || 'Unknown'}) - Gen ${p.generation || 1}`
).join('\n')}
`.trim();
}
return plants;
}
async function exportTransport(format: ExportFormat, plantId?: string): Promise<any> {
// Transport data would come from transport blockchain
// For now, return placeholder
if (format === 'csv') {
return 'PlantID,EventType,From,To,Distance,Carbon,Timestamp\n';
}
if (format === 'summary') {
return 'TRANSPORT EXPORT\n================\nNo transport data available';
}
return [];
}
async function exportSignatures(format: ExportFormat): Promise<any> {
const signatureManager = getSignatureManager();
const stats = signatureManager.getStats();
const identities = signatureManager.getAllIdentities();
if (format === 'csv') {
const headers = ['ID', 'Name', 'Type', 'Verified', 'Created'];
const rows = identities.map(i => [
i.id,
i.name,
i.type,
i.verified ? 'Yes' : 'No',
i.createdAt
]);
return [headers.join(','), ...rows.map(r => r.join(','))].join('\n');
}
if (format === 'summary') {
return `
SIGNATURES EXPORT
=================
Total Identities: ${stats.totalIdentities}
Verified Identities: ${stats.verifiedIdentities}
Total Signatures: ${stats.totalSignatures}
Pending Multi-Sigs: ${stats.pendingMultiSigs}
Completed Multi-Sigs: ${stats.completedMultiSigs}
Signatures by Type:
${Object.entries(stats.signaturesByType).map(([t, c]) => ` ${t}: ${c}`).join('\n') || ' None'}
`.trim();
}
return { stats, identities };
}
async function exportFull(format: ExportFormat): Promise<any> {
const dashboard = await exportDashboard('json');
const audit = await exportAudit('json');
const events = await exportEvents('json');
const plants = await exportPlants('json');
const transport = await exportTransport('json');
const signatures = await exportSignatures('json');
if (format === 'summary') {
return `
FULL TRANSPARENCY EXPORT
========================
Exported: ${new Date().toISOString()}
This export contains all transparency data from LocalGreenChain.
Sections Included:
- Dashboard metrics
- Audit log entries
- Event stream data
- Plant registry
- Transport history
- Digital signatures
Use JSON format for complete data export.
`.trim();
}
if (format === 'csv') {
return 'Full export not available in CSV format. Use JSON instead.';
}
return {
dashboard,
audit,
events,
plants,
transport,
signatures
};
}