Spaces:
Runtime error
Runtime error
| // Virtual SSD Web Interface JavaScript | |
| // Utility functions | |
| function showMessage(message, type = 'success') { | |
| const messagesDiv = document.getElementById('messages'); | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = `message ${type}`; | |
| messageDiv.textContent = message; | |
| messagesDiv.appendChild(messageDiv); | |
| setTimeout(() => { | |
| messageDiv.remove(); | |
| }, 5000); | |
| } | |
| function formatBytes(bytes) { | |
| if (bytes === 0) return '0 Bytes'; | |
| const k = 1024; | |
| const sizes = ['Bytes', 'KB', 'MB', 'GB']; | |
| const i = Math.floor(Math.log(bytes) / Math.log(k)); | |
| return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; | |
| } | |
| function showLoading(containerId) { | |
| const container = document.getElementById(containerId); | |
| container.innerHTML = ` | |
| <div class="loading" style="display: block;"> | |
| <div class="spinner"></div> | |
| Loading... | |
| </div> | |
| `; | |
| } | |
| // API functions | |
| async function apiCall(url, options = {}) { | |
| try { | |
| const response = await fetch(url, options); | |
| const data = await response.json(); | |
| if (!data.success) { | |
| throw new Error(data.error || 'Unknown error occurred'); | |
| } | |
| return data; | |
| } catch (error) { | |
| console.error('API call failed:', error); | |
| showMessage(`Error: ${error.message}`, 'error'); | |
| throw error; | |
| } | |
| } | |
| // File operations | |
| async function uploadFile() { | |
| const fileInput = document.getElementById('fileInput'); | |
| const filenameInput = document.getElementById('filenameInput'); | |
| if (!fileInput.files.length) { | |
| showMessage('Please select a file to upload', 'error'); | |
| return; | |
| } | |
| const file = fileInput.files[0]; | |
| const filename = filenameInput.value.trim() || file.name; | |
| const formData = new FormData(); | |
| formData.append('file', file); | |
| try { | |
| showMessage('Uploading file...', 'success'); | |
| await apiCall(`/api/ssd/files/${encodeURIComponent(filename)}`, { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| showMessage(`File "${filename}" uploaded successfully!`, 'success'); | |
| fileInput.value = ''; | |
| filenameInput.value = ''; | |
| refreshFiles(); | |
| refreshCapacity(); | |
| } catch (error) { | |
| // Error already shown by apiCall | |
| } | |
| } | |
| async function downloadFile(filename) { | |
| try { | |
| const response = await fetch(`/api/ssd/files/${encodeURIComponent(filename)}`); | |
| if (!response.ok) { | |
| throw new Error('Download failed'); | |
| } | |
| const blob = await response.blob(); | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = filename; | |
| document.body.appendChild(a); | |
| a.click(); | |
| window.URL.revokeObjectURL(url); | |
| document.body.removeChild(a); | |
| showMessage(`File "${filename}" downloaded successfully!`, 'success'); | |
| } catch (error) { | |
| showMessage(`Failed to download "${filename}": ${error.message}`, 'error'); | |
| } | |
| } | |
| async function deleteFile(filename) { | |
| if (!confirm(`Are you sure you want to delete "${filename}"?`)) { | |
| return; | |
| } | |
| try { | |
| await apiCall(`/api/ssd/files/${encodeURIComponent(filename)}`, { | |
| method: 'DELETE' | |
| }); | |
| showMessage(`File "${filename}" deleted successfully!`, 'success'); | |
| refreshFiles(); | |
| refreshCapacity(); | |
| } catch (error) { | |
| // Error already shown by apiCall | |
| } | |
| } | |
| async function formatSSD() { | |
| if (!confirm('Are you sure you want to format the Virtual SSD? This will delete ALL files!')) { | |
| return; | |
| } | |
| try { | |
| await apiCall('/api/ssd/format', { | |
| method: 'POST' | |
| }); | |
| showMessage('Virtual SSD formatted successfully!', 'success'); | |
| refreshFiles(); | |
| refreshCapacity(); | |
| refreshStats(); | |
| } catch (error) { | |
| // Error already shown by apiCall | |
| } | |
| } | |
| // Display functions | |
| async function refreshFiles() { | |
| showLoading('filesContainer'); | |
| try { | |
| const data = await apiCall('/api/ssd/files'); | |
| const files = data.files; | |
| const container = document.getElementById('filesContainer'); | |
| if (Object.keys(files).length === 0) { | |
| container.innerHTML = '<p style="text-align: center; color: #6c757d; padding: 40px;">No files found on the Virtual SSD</p>'; | |
| return; | |
| } | |
| container.innerHTML = Object.entries(files).map(([filename, info]) => ` | |
| <div class="file-card"> | |
| <div class="file-name">${filename}</div> | |
| <div class="file-info"> | |
| Size: ${formatBytes(info.size)}<br> | |
| Blocks: ${info.blocks.length} | |
| </div> | |
| <button onclick="downloadFile('${filename.replace(/'/g, "\\'")}')" class="btn btn-primary">📥 Download</button> | |
| <button onclick="deleteFile('${filename.replace(/'/g, "\\'")}')" class="btn btn-danger">🗑️ Delete</button> | |
| </div> | |
| `).join(''); | |
| } catch (error) { | |
| document.getElementById('filesContainer').innerHTML = '<p style="color: #e74c3c;">Failed to load files</p>'; | |
| } | |
| } | |
| async function refreshCapacity() { | |
| showLoading('capacityInfo'); | |
| try { | |
| const data = await apiCall('/api/ssd/capacity'); | |
| const capacity = data.capacity; | |
| const usagePercent = capacity.usage_percent || 0; | |
| document.getElementById('capacityInfo').innerHTML = ` | |
| <h3>Storage Usage</h3> | |
| <div class="capacity-bar"> | |
| <div class="capacity-fill" style="width: ${usagePercent}%"></div> | |
| </div> | |
| <p><strong>Total:</strong> ${capacity.total_gb} GB</p> | |
| <p><strong>Used:</strong> ${capacity.used_gb} GB</p> | |
| <p><strong>Free:</strong> ${capacity.free_gb} GB</p> | |
| <p><strong>Usage:</strong> ${usagePercent.toFixed(2)}%</p> | |
| `; | |
| } catch (error) { | |
| document.getElementById('capacityInfo').innerHTML = '<p style="color: #e74c3c;">Failed to load capacity information</p>'; | |
| } | |
| } | |
| async function refreshStats() { | |
| showLoading('statsContainer'); | |
| try { | |
| const data = await apiCall('/api/ssd/stats'); | |
| const stats = data.stats; | |
| const container = document.getElementById('statsContainer'); | |
| container.innerHTML = ` | |
| <div class="stat-card"> | |
| <div class="stat-title">Flash Statistics</div> | |
| <div class="stat-value"> | |
| Used: ${formatBytes(stats.flash_stats.used_bytes)}<br> | |
| Free: ${formatBytes(stats.flash_stats.free_bytes)}<br> | |
| Usage: ${stats.flash_stats.usage_percent.toFixed(2)}%<br> | |
| Bad Blocks: ${stats.flash_stats.bad_blocks} | |
| </div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-title">File System</div> | |
| <div class="stat-value"> | |
| Total Blocks: ${stats.file_system_stats.total_blocks.toLocaleString()}<br> | |
| Used Blocks: ${stats.file_system_stats.used_blocks.toLocaleString()}<br> | |
| Free Blocks: ${stats.file_system_stats.free_blocks.toLocaleString()} | |
| </div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-title">FTL Statistics</div> | |
| <div class="stat-value"> | |
| Logical Blocks: ${stats.ftl_stats.total_logical_blocks.toLocaleString()}<br> | |
| Physical Blocks: ${stats.ftl_stats.total_physical_blocks.toLocaleString()}<br> | |
| Mapped Blocks: ${stats.ftl_stats.mapped_blocks.toLocaleString()} | |
| </div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-title">RAM Buffer</div> | |
| <div class="stat-value"> | |
| Capacity: ${formatBytes(stats.ram_buffer_stats.capacity_bytes)}<br> | |
| Used: ${formatBytes(stats.ram_buffer_stats.used_bytes)}<br> | |
| Items: ${stats.ram_buffer_stats.items_count}<br> | |
| Hit Rate: ${(stats.ram_buffer_stats.hit_rate * 100).toFixed(2)}% | |
| </div> | |
| </div> | |
| `; | |
| } catch (error) { | |
| document.getElementById('statsContainer').innerHTML = '<p style="color: #e74c3c;">Failed to load statistics</p>'; | |
| } | |
| } | |
| // Initialize the page | |
| document.addEventListener('DOMContentLoaded', function() { | |
| refreshFiles(); | |
| refreshCapacity(); | |
| refreshStats(); | |
| // Auto-refresh every 30 seconds | |
| setInterval(() => { | |
| refreshCapacity(); | |
| }, 30000); | |
| }); | |