defarm-sdk
Version:
DeFarm SDK - On-premise blockchain data processing and tokenization engine for agriculture supply chain
289 lines (248 loc) • 7.92 kB
JavaScript
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);
});
}