UNPKG

defarm-sdk

Version:

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

958 lines (835 loc) 31.6 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DeFarm SDK Dashboard</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background: #0a0b0d; min-height: 100vh; color: #e4e4e7; } .container { max-width: 1400px; margin: 0 auto; padding: 20px; } header { background: rgba(24, 24, 27, 0.8); backdrop-filter: blur(10px); border: 1px solid rgba(63, 63, 70, 0.5); border-radius: 12px; padding: 20px 30px; margin-bottom: 30px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); display: flex; justify-content: space-between; align-items: center; } .logo { display: flex; align-items: center; gap: 15px; } .logo h1 { color: #10b981; font-size: 28px; font-weight: 700; } .logo .version { background: rgba(16, 185, 129, 0.1); border: 1px solid rgba(16, 185, 129, 0.3); padding: 4px 10px; border-radius: 20px; font-size: 12px; color: #10b981; } .status-bar { display: flex; gap: 15px; } .status-item { display: flex; align-items: center; gap: 8px; padding: 8px 15px; background: rgba(39, 39, 42, 0.5); border: 1px solid rgba(63, 63, 70, 0.5); border-radius: 20px; font-size: 14px; color: #a1a1aa; } .status-indicator { width: 10px; height: 10px; border-radius: 50%; animation: pulse 2s infinite; } .status-indicator.connected { background: #10b981; box-shadow: 0 0 10px rgba(16, 185, 129, 0.5); } .status-indicator.disconnected { background: #ef4444; box-shadow: 0 0 10px rgba(239, 68, 68, 0.5); } .status-indicator.initializing { background: #f59e0b; box-shadow: 0 0 10px rgba(245, 158, 11, 0.5); } @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } } .dashboard-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 30px; } .metric-card { background: rgba(24, 24, 27, 0.8); backdrop-filter: blur(10px); border: 1px solid rgba(63, 63, 70, 0.5); border-radius: 12px; padding: 25px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); transition: all 0.3s ease; } .metric-card:hover { transform: translateY(-5px); border-color: rgba(16, 185, 129, 0.3); box-shadow: 0 15px 40px rgba(16, 185, 129, 0.1); } .metric-label { color: #71717a; font-size: 12px; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 1.5px; font-weight: 600; } .metric-value { font-size: 36px; font-weight: bold; color: #fafafa; margin-bottom: 10px; } .metric-change { font-size: 14px; color: #10b981; } .metric-change.negative { color: #ef4444; } .chart-container { background: rgba(24, 24, 27, 0.8); backdrop-filter: blur(10px); border: 1px solid rgba(63, 63, 70, 0.5); border-radius: 12px; padding: 25px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); margin-bottom: 30px; } .chart-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } .chart-title { font-size: 18px; font-weight: 600; color: #fafafa; } .chart-options { display: flex; gap: 10px; } .chart-option { padding: 6px 12px; background: rgba(39, 39, 42, 0.5); border: 1px solid rgba(63, 63, 70, 0.5); color: #a1a1aa; border-radius: 8px; cursor: pointer; font-size: 12px; transition: all 0.3s ease; } .chart-option:hover { background: rgba(39, 39, 42, 0.8); border-color: rgba(16, 185, 129, 0.3); } .chart-option.active { background: rgba(16, 185, 129, 0.2); border-color: #10b981; color: #10b981; } #realtimeChart { height: 300px; position: relative; } .events-list { background: rgba(24, 24, 27, 0.8); backdrop-filter: blur(10px); border: 1px solid rgba(63, 63, 70, 0.5); border-radius: 12px; padding: 25px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); max-height: 400px; overflow-y: auto; } .event-item { display: flex; align-items: center; padding: 12px; border-bottom: 1px solid rgba(63, 63, 70, 0.3); transition: background 0.3s ease; } .event-item:hover { background: rgba(39, 39, 42, 0.3); } .event-icon { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; margin-right: 15px; font-size: 20px; } .event-icon.process { background: rgba(59, 130, 246, 0.1); border: 1px solid rgba(59, 130, 246, 0.3); color: #3b82f6; } .event-icon.tokenize { background: rgba(16, 185, 129, 0.1); border: 1px solid rgba(16, 185, 129, 0.3); color: #10b981; } .event-icon.error { background: rgba(239, 68, 68, 0.1); border: 1px solid rgba(239, 68, 68, 0.3); color: #ef4444; } .event-details { flex: 1; } .event-title { font-weight: 600; margin-bottom: 4px; color: #e4e4e7; } .event-time { font-size: 12px; color: #71717a; } .control-panel { background: rgba(24, 24, 27, 0.8); backdrop-filter: blur(10px); border: 1px solid rgba(63, 63, 70, 0.5); border-radius: 12px; padding: 25px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); margin-bottom: 30px; } .control-buttons { display: flex; gap: 15px; flex-wrap: wrap; } .btn { padding: 12px 24px; border: none; border-radius: 10px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.3s ease; text-transform: uppercase; letter-spacing: 1px; } .btn-primary { background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: white; border: none; } .btn-primary:hover { background: linear-gradient(135deg, #059669 0%, #047857 100%); transform: translateY(-2px); box-shadow: 0 5px 20px rgba(16, 185, 129, 0.4); } .btn-success { background: rgba(16, 185, 129, 0.1); color: #10b981; border: 1px solid rgba(16, 185, 129, 0.3); } .btn-success:hover { background: rgba(16, 185, 129, 0.2); border-color: #10b981; box-shadow: 0 5px 15px rgba(16, 185, 129, 0.2); } .btn-danger { background: rgba(239, 68, 68, 0.1); color: #ef4444; border: 1px solid rgba(239, 68, 68, 0.3); } .btn-danger:hover { background: rgba(239, 68, 68, 0.2); border-color: #ef4444; box-shadow: 0 5px 15px rgba(239, 68, 68, 0.2); } .btn:disabled { opacity: 0.5; cursor: not-allowed; } .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); justify-content: center; align-items: center; z-index: 1000; } .modal.active { display: flex; } .modal-content { background: #18181b; border: 1px solid rgba(63, 63, 70, 0.5); border-radius: 12px; padding: 30px; max-width: 500px; width: 90%; max-height: 80vh; overflow-y: auto; } .modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } .modal-title { font-size: 24px; font-weight: 600; color: #fafafa; } .modal-close { background: none; border: none; font-size: 28px; cursor: pointer; color: #71717a; } .form-group { margin-bottom: 20px; } .form-label { display: block; margin-bottom: 8px; font-weight: 600; color: #e4e4e7; } .form-input { width: 100%; padding: 10px 15px; background: rgba(39, 39, 42, 0.5); border: 1px solid rgba(63, 63, 70, 0.5); color: #e4e4e7; border-radius: 10px; font-size: 14px; transition: all 0.3s ease; } .form-input:focus { outline: none; border-color: #10b981; background: rgba(39, 39, 42, 0.8); } .form-select { width: 100%; padding: 10px 15px; background: rgba(39, 39, 42, 0.5); border: 1px solid rgba(63, 63, 70, 0.5); color: #e4e4e7; border-radius: 10px; font-size: 14px; cursor: pointer; } .form-select option { background: #18181b; color: #e4e4e7; } @media (max-width: 768px) { .dashboard-grid { grid-template-columns: 1fr; } header { flex-direction: column; gap: 20px; } .status-bar { width: 100%; justify-content: center; } } </style> </head> <body> <div class="container"> <header> <div class="logo"> <h1>🌾 DeFarm SDK</h1> <span class="version">v1.0.0</span> </div> <div class="status-bar"> <div class="status-item"> <div class="status-indicator" id="systemStatus"></div> <span id="systemStatusText">System</span> </div> <div class="status-item"> <div class="status-indicator" id="dbStatus"></div> <span id="dbStatusText">Database</span> </div> <div class="status-item"> <div class="status-indicator" id="blockchainStatus"></div> <span id="blockchainStatusText">Blockchain</span> </div> </div> </header> <div class="control-panel"> <div class="chart-header"> <h2 class="chart-title">Control Panel</h2> </div> <div class="control-buttons"> <button class="btn btn-primary" onclick="showInitModal()">Initialize SDK</button> <button class="btn btn-success" onclick="testProcessing()">Test Processing</button> <button class="btn btn-success" onclick="testTokenization()">Test Tokenization</button> <button class="btn btn-danger" onclick="shutdownSDK()">Shutdown SDK</button> </div> </div> <div class="dashboard-grid"> <div class="metric-card"> <div class="metric-label">Total Assets</div> <div class="metric-value" id="totalAssets">0</div> <div class="metric-change">+0% from last hour</div> </div> <div class="metric-card"> <div class="metric-label">Tokenizations</div> <div class="metric-value" id="totalTokenizations">0</div> <div class="metric-change">+0% from last hour</div> </div> <div class="metric-card"> <div class="metric-label">Processing Rate</div> <div class="metric-value" id="processingRate">0</div> <div class="metric-change">per minute</div> </div> <div class="metric-card"> <div class="metric-label">Error Rate</div> <div class="metric-value" id="errorRate">0%</div> <div class="metric-change negative" id="errorTrend"></div> </div> </div> <div class="chart-container"> <div class="chart-header"> <h2 class="chart-title">Real-time Activity</h2> <div class="chart-options"> <button class="chart-option active">1H</button> <button class="chart-option">24H</button> <button class="chart-option">7D</button> </div> </div> <div id="realtimeChart"> <canvas id="activityCanvas" width="1000" height="300"></canvas> </div> </div> <div class="events-list"> <div class="chart-header"> <h2 class="chart-title">Recent Events</h2> </div> <div id="eventsList"></div> </div> </div> <!-- Initialize SDK Modal --> <div class="modal" id="initModal"> <div class="modal-content"> <div class="modal-header"> <h3 class="modal-title">Initialize SDK</h3> <button class="modal-close" onclick="closeModal('initModal')">&times;</button> </div> <form id="initForm"> <div class="form-group"> <label class="form-label">Deployment Mode</label> <select class="form-select" name="deploymentMode"> <option value="hybrid">Hybrid (Recommended)</option> <option value="on-premise">On-Premise</option> <option value="cloud">Cloud</option> </select> </div> <div class="form-group"> <label class="form-label">Database Type</label> <select class="form-select" name="dbType"> <option value="postgresql">PostgreSQL</option> <option value="oracle">Oracle</option> <option value="sqlserver">SQL Server</option> <option value="mysql">MySQL</option> </select> </div> <div class="form-group"> <label class="form-label">Database Host</label> <input class="form-input" type="text" name="dbHost" value="localhost" /> </div> <div class="form-group"> <label class="form-label">Database Name</label> <input class="form-input" type="text" name="dbName" value="defarm_test" /> </div> <div class="form-group"> <label class="form-label">Enable Blockchain</label> <select class="form-select" name="blockchainEnabled"> <option value="true">Yes</option> <option value="false">No</option> </select> </div> <div class="form-group"> <label class="form-label">Enable Relay (Gas-free)</label> <select class="form-select" name="relayEnabled"> <option value="true">Yes</option> <option value="false">No</option> </select> </div> <button type="submit" class="btn btn-primary">Initialize</button> </form> </div> </div> <script> let ws = null; let metrics = {}; let chartData = []; const maxDataPoints = 60; // WebSocket connection function connectWebSocket() { ws = new WebSocket(`ws://${window.location.hostname}:${window.location.port || 3456}`); ws.onopen = () => { console.log('Connected to dashboard server'); updateConnectionStatus(true); }; ws.onmessage = (event) => { const message = JSON.parse(event.data); if (message.type === 'metrics') { updateMetrics(message.data); } }; ws.onerror = (error) => { console.error('WebSocket error:', error); updateConnectionStatus(false); }; ws.onclose = () => { console.log('Disconnected from server'); updateConnectionStatus(false); // Reconnect after 3 seconds setTimeout(connectWebSocket, 3000); }; } function updateConnectionStatus(connected) { const indicator = document.getElementById('systemStatus'); if (connected) { indicator.className = 'status-indicator connected'; } else { indicator.className = 'status-indicator disconnected'; } } function updateMetrics(data) { metrics = data; // Update metric cards document.getElementById('totalAssets').textContent = data.totalAssets || 0; document.getElementById('totalTokenizations').textContent = data.totalTokenizations || 0; document.getElementById('processingRate').textContent = data.processingRate || 0; const errorPercent = data.totalAssets > 0 ? ((data.errorRate / data.totalAssets) * 100).toFixed(1) : 0; document.getElementById('errorRate').textContent = errorPercent + '%'; // Update status indicators updateStatusIndicators(data); // Update events list updateEventsList(data.recentEvents || []); // Update chart updateChart(data); } function updateStatusIndicators(data) { // System status const systemIndicator = document.getElementById('systemStatus'); const systemText = document.getElementById('systemStatusText'); if (data.systemStatus === 'operational') { systemIndicator.className = 'status-indicator connected'; systemText.textContent = 'System: Online'; } else if (data.systemStatus === 'initializing') { systemIndicator.className = 'status-indicator initializing'; systemText.textContent = 'System: Initializing'; } else { systemIndicator.className = 'status-indicator disconnected'; systemText.textContent = 'System: Offline'; } // Database status const dbIndicator = document.getElementById('dbStatus'); const dbText = document.getElementById('dbStatusText'); if (data.databaseStatus === 'connected') { dbIndicator.className = 'status-indicator connected'; dbText.textContent = 'Database: Connected'; } else { dbIndicator.className = 'status-indicator disconnected'; dbText.textContent = 'Database: Disconnected'; } // Blockchain status const blockchainIndicator = document.getElementById('blockchainStatus'); const blockchainText = document.getElementById('blockchainStatusText'); if (data.blockchainStatus === 'connected') { blockchainIndicator.className = 'status-indicator connected'; blockchainText.textContent = 'Blockchain: Connected'; } else if (data.blockchainStatus === 'disabled') { blockchainIndicator.className = 'status-indicator disconnected'; blockchainText.textContent = 'Blockchain: Disabled'; } else { blockchainIndicator.className = 'status-indicator disconnected'; blockchainText.textContent = 'Blockchain: Disconnected'; } } function updateEventsList(events) { const container = document.getElementById('eventsList'); container.innerHTML = ''; events.slice(0, 10).forEach(event => { const item = document.createElement('div'); item.className = 'event-item'; const icon = document.createElement('div'); icon.className = `event-icon ${event.type}`; icon.textContent = getEventIcon(event.type); const details = document.createElement('div'); details.className = 'event-details'; const title = document.createElement('div'); title.className = 'event-title'; title.textContent = getEventTitle(event); const time = document.createElement('div'); time.className = 'event-time'; time.textContent = formatTime(event.timestamp); details.appendChild(title); details.appendChild(time); item.appendChild(icon); item.appendChild(details); container.appendChild(item); }); } function getEventIcon(type) { switch(type) { case 'process': return '⚙️'; case 'tokenize': return '🪙'; case 'error': return '❌'; default: return '📋'; } } function getEventTitle(event) { const base = event.type.charAt(0).toUpperCase() + event.type.slice(1); return event.success ? `${base} Successful` : `${base} Failed`; } function formatTime(timestamp) { const date = new Date(timestamp); const now = new Date(); const diff = now - date; if (diff < 60000) { return 'Just now'; } else if (diff < 3600000) { return Math.floor(diff / 60000) + ' minutes ago'; } else { return date.toLocaleTimeString(); } } function updateChart(data) { // Add new data point chartData.push({ time: Date.now(), assets: data.totalAssets, tokenizations: data.totalTokenizations, errors: data.errorRate }); // Keep only last N points if (chartData.length > maxDataPoints) { chartData.shift(); } drawChart(); } function drawChart() { const canvas = document.getElementById('activityCanvas'); const ctx = canvas.getContext('2d'); // Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height); if (chartData.length < 2) return; // Draw grid ctx.strokeStyle = 'rgba(63, 63, 70, 0.3)'; ctx.lineWidth = 1; for (let i = 0; i <= 5; i++) { const y = (canvas.height / 5) * i; ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(canvas.width, y); ctx.stroke(); } // Draw data lines const xStep = canvas.width / (maxDataPoints - 1); const maxValue = Math.max(...chartData.map(d => d.assets), 10); // Assets line with gradient const gradient1 = ctx.createLinearGradient(0, 0, 0, canvas.height); gradient1.addColorStop(0, 'rgba(16, 185, 129, 0.8)'); gradient1.addColorStop(1, 'rgba(16, 185, 129, 0.1)'); // Assets line ctx.strokeStyle = '#10b981'; ctx.lineWidth = 2; ctx.beginPath(); chartData.forEach((point, index) => { const x = index * xStep; const y = canvas.height - (point.assets / maxValue) * canvas.height; if (index === 0) { ctx.moveTo(x, y); } else { ctx.lineTo(x, y); } }); ctx.stroke(); // Tokenizations line ctx.strokeStyle = '#3b82f6'; ctx.lineWidth = 2; ctx.beginPath(); chartData.forEach((point, index) => { const x = index * xStep; const y = canvas.height - (point.tokenizations / maxValue) * canvas.height; if (index === 0) { ctx.moveTo(x, y); } else { ctx.lineTo(x, y); } }); ctx.stroke(); } // Modal functions function showInitModal() { document.getElementById('initModal').classList.add('active'); } function closeModal(modalId) { document.getElementById(modalId).classList.remove('active'); } // SDK control functions document.getElementById('initForm').addEventListener('submit', async (e) => { e.preventDefault(); const formData = new FormData(e.target); const config = { deploymentMode: formData.get('deploymentMode'), database: { type: formData.get('dbType'), host: formData.get('dbHost'), database: formData.get('dbName'), user: 'test_user', password: 'test_password' }, blockchain: { enabled: formData.get('blockchainEnabled') === 'true' }, relay: { enabled: formData.get('relayEnabled') === 'true' } }; try { const response = await fetch('/api/sdk/initialize', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config) }); const result = await response.json(); if (result.success) { alert('SDK initialized successfully!'); closeModal('initModal'); } else { alert('Error: ' + result.error); } } catch (error) { alert('Failed to initialize SDK: ' + error.message); } }); async function testProcessing() { const testData = { asset_type: 'livestock', breed: 'Angus', weight: 450 + Math.random() * 100, location: 'Test Farm', timestamp: Date.now() }; try { const response = await fetch('/api/process', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(testData) }); const result = await response.json(); if (response.ok) { alert('Processing successful!'); } else { alert('Processing failed: ' + result.error); } } catch (error) { alert('Error: ' + error.message); } } async function testTokenization() { const testData = { asset_type: 'cattle', quantity: Math.floor(Math.random() * 100) + 1, location: 'Brazil', certifications: ['organic'] }; try { const response = await fetch('/api/tokenize', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(testData) }); const result = await response.json(); if (response.ok) { alert('Tokenization successful! Token ID: ' + (result.tokenId || 'Pending')); } else { alert('Tokenization failed: ' + result.error); } } catch (error) { alert('Error: ' + error.message); } } async function shutdownSDK() { if (!confirm('Are you sure you want to shutdown the SDK?')) return; try { const response = await fetch('/api/sdk/shutdown', { method: 'POST' }); const result = await response.json(); if (result.success) { alert('SDK shutdown successfully'); } else { alert('Error: ' + result.error); } } catch (error) { alert('Failed to shutdown: ' + error.message); } } // Initialize connectWebSocket(); </script> </body> </html>