UNPKG

defarm-sdk

Version:

DeFarm SDK - On-premise blockchain data processing and tokenization engine for agriculture supply chain

289 lines (248 loc) 7.92 kB
const express = require('express'); const http = require('http'); const WebSocket = require('ws'); const path = require('path'); const { DeFarmSDK } = require('../../index'); class DashboardServer { constructor(port = 3456) { this.port = port; this.app = express(); this.server = http.createServer(this.app); this.wss = new WebSocket.Server({ server: this.server }); this.sdk = null; this.metrics = { totalAssets: 0, totalTokenizations: 0, recentEvents: [], systemStatus: 'initializing', blockchainStatus: 'disconnected', databaseStatus: 'disconnected', activeConnections: 0, processingRate: 0, errorRate: 0, lastUpdate: Date.now() }; this.clients = new Set(); this.setupMiddleware(); this.setupRoutes(); this.setupWebSocket(); } setupMiddleware() { this.app.use(express.json()); this.app.use(express.static(path.join(__dirname, 'public'))); // CORS for local development this.app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Content-Type'); next(); }); } setupRoutes() { // Main dashboard page this.app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html')); }); // API endpoints this.app.get('/api/metrics', (req, res) => { res.json(this.metrics); }); this.app.get('/api/stats', async (req, res) => { if (!this.sdk) { return res.status(503).json({ error: 'SDK not initialized' }); } try { const stats = await this.sdk.getStatistics(); res.json(stats); } catch (error) { res.status(500).json({ error: error.message }); } }); this.app.get('/api/assets', async (req, res) => { if (!this.sdk) { return res.status(503).json({ error: 'SDK not initialized' }); } try { const assets = await this.sdk.queryData({ limit: 100, order: 'timestamp DESC' }); res.json(assets); } catch (error) { res.status(500).json({ error: error.message }); } }); this.app.post('/api/process', async (req, res) => { if (!this.sdk) { return res.status(503).json({ error: 'SDK not initialized' }); } try { const result = await this.sdk.processAgricultureData(req.body); this.updateMetrics('process', { success: true }); res.json(result); } catch (error) { this.updateMetrics('process', { success: false, error: error.message }); res.status(500).json({ error: error.message }); } }); this.app.post('/api/tokenize', async (req, res) => { if (!this.sdk) { return res.status(503).json({ error: 'SDK not initialized' }); } try { const result = await this.sdk.createAssetToken(req.body); this.updateMetrics('tokenize', { success: true }); res.json(result); } catch (error) { this.updateMetrics('tokenize', { success: false, error: error.message }); res.status(500).json({ error: error.message }); } }); this.app.get('/api/config', (req, res) => { if (!this.sdk) { return res.status(503).json({ error: 'SDK not initialized' }); } // Return sanitized config (no passwords/keys) const config = { ...this.sdk.config }; if (config.database) { delete config.database.password; } if (config.blockchain) { delete config.blockchain.privateKey; } if (config.relay) { delete config.relay.apiKey; } res.json(config); }); this.app.post('/api/sdk/initialize', async (req, res) => { try { this.sdk = new DeFarmSDK(req.body); await this.sdk.initialize(); this.metrics.systemStatus = 'operational'; this.metrics.databaseStatus = 'connected'; this.metrics.blockchainStatus = this.sdk.config.blockchain.enabled ? 'connected' : 'disabled'; this.broadcastMetrics(); res.json({ success: true, message: 'SDK initialized successfully' }); } catch (error) { this.metrics.systemStatus = 'error'; this.broadcastMetrics(); res.status(500).json({ error: error.message }); } }); this.app.post('/api/sdk/shutdown', async (req, res) => { if (!this.sdk) { return res.status(503).json({ error: 'SDK not initialized' }); } try { await this.sdk.shutdown(); this.sdk = null; this.metrics.systemStatus = 'shutdown'; this.metrics.databaseStatus = 'disconnected'; this.metrics.blockchainStatus = 'disconnected'; this.broadcastMetrics(); res.json({ success: true, message: 'SDK shutdown successfully' }); } catch (error) { res.status(500).json({ error: error.message }); } }); } setupWebSocket() { this.wss.on('connection', (ws) => { this.clients.add(ws); this.metrics.activeConnections = this.clients.size; // Send initial metrics ws.send(JSON.stringify({ type: 'metrics', data: this.metrics })); ws.on('close', () => { this.clients.delete(ws); this.metrics.activeConnections = this.clients.size; this.broadcastMetrics(); }); ws.on('error', (error) => { console.error('WebSocket error:', error); this.clients.delete(ws); }); }); } updateMetrics(event, data = {}) { // Update counters if (event === 'process') { if (data.success) { this.metrics.totalAssets++; this.metrics.processingRate++; } else { this.metrics.errorRate++; } } else if (event === 'tokenize') { if (data.success) { this.metrics.totalTokenizations++; } else { this.metrics.errorRate++; } } // Add to recent events this.metrics.recentEvents.unshift({ type: event, success: data.success, timestamp: Date.now(), details: data }); // Keep only last 50 events if (this.metrics.recentEvents.length > 50) { this.metrics.recentEvents = this.metrics.recentEvents.slice(0, 50); } this.metrics.lastUpdate = Date.now(); this.broadcastMetrics(); } broadcastMetrics() { const message = JSON.stringify({ type: 'metrics', data: this.metrics }); this.clients.forEach(client => { if (client.readyState === WebSocket.OPEN) { client.send(message); } }); } async start() { return new Promise((resolve) => { this.server.listen(this.port, () => { console.log(`🚀 DeFarm Dashboard running at http://localhost:${this.port}`); console.log(`📊 WebSocket endpoint: ws://localhost:${this.port}`); resolve(); }); }); } async stop() { return new Promise((resolve) => { // Close all WebSocket connections this.clients.forEach(client => client.close()); this.wss.close(); // Shutdown SDK if initialized if (this.sdk) { this.sdk.shutdown().catch(console.error); } // Close HTTP server this.server.close(() => { console.log('Dashboard server stopped'); resolve(); }); }); } } // Export for use in CLI and other modules module.exports = { DashboardServer }; // Start server if run directly if (require.main === module) { const server = new DashboardServer(); server.start().catch(console.error); // Graceful shutdown process.on('SIGINT', async () => { console.log('\nShutting down dashboard...'); await server.stop(); process.exit(0); }); }