File storage-module.js of Package PersistenceOS
/**
* PersistenceOS Storage Management Module
* Handles storage pools, disk operations, and storage-related functionality
*
* Depends on: core-utils.js, auth-module.js
*/
// =============================================================================
// STORAGE STATE MANAGEMENT
// =============================================================================
const storageState = {
storagePools: [],
isLoading: false,
refreshInterval: null,
lastUpdate: null
};
// =============================================================================
// STORAGE DATA FUNCTIONS
// =============================================================================
/**
* Refresh storage data from API
* @returns {Promise<void>}
*/
async function refreshStorage() {
storageState.isLoading = true;
console.log('๐ Refreshing storage data...');
try {
const data = await PersistenceCore.apiGet('/storage/');
console.log('โ
Storage data loaded:', data);
// Transform API data to match UI format
storageState.storagePools = (data.pools || []).map(pool => ({
name: pool.name || 'Unknown',
status: pool.status || 'unknown',
type: pool.type || 'unknown',
size: pool.size || '0 GB',
used: pool.used || '0 GB',
available: pool.available || '0 GB',
usedPercent: pool.used_percent || 0,
health: pool.health || 'unknown',
mountPoint: pool.mount_point || '',
filesystem: pool.filesystem || 'unknown'
}));
console.log(`๐ Updated storage pools: ${storageState.storagePools.length} pools found`);
if (storageState.storagePools.length === 0) {
PersistenceCore.showNotification('โน๏ธ No storage pools found', 'info');
} else {
const healthyPools = storageState.storagePools.filter(p => p.status === 'healthy').length;
PersistenceCore.showNotification(`โ
Found ${storageState.storagePools.length} storage pools (${healthyPools} healthy)`, 'success');
}
storageState.lastUpdate = new Date();
// Trigger custom event for Vue.js reactivity
window.dispatchEvent(new CustomEvent('storage-data-updated', {
detail: { pools: storageState.storagePools, stats: getStorageStats() }
}));
} catch (error) {
console.error('โ Error refreshing storage:', error);
PersistenceCore.showNotification('โ Error refreshing storage data', 'error');
} finally {
storageState.isLoading = false;
}
}
/**
* Get storage statistics
* @returns {object}
*/
function getStorageStats() {
const pools = storageState.storagePools;
const healthy = pools.filter(p => p.status === 'healthy').length;
const degraded = pools.filter(p => p.status === 'degraded').length;
const total = pools.length;
// Calculate total storage usage
let totalSize = 0;
let totalUsed = 0;
pools.forEach(pool => {
// Parse size strings (e.g., "100 GB" -> 100)
const sizeMatch = pool.size.match(/(\d+(?:\.\d+)?)/);
const usedMatch = pool.used.match(/(\d+(?:\.\d+)?)/);
if (sizeMatch) totalSize += parseFloat(sizeMatch[1]);
if (usedMatch) totalUsed += parseFloat(usedMatch[1]);
});
const overallUsage = totalSize > 0 ? Math.round((totalUsed / totalSize) * 100) : 0;
return {
total,
healthy,
degraded,
totalSize: `${totalSize.toFixed(1)} GB`,
totalUsed: `${totalUsed.toFixed(1)} GB`,
overallUsage
};
}
// =============================================================================
// STORAGE POOL OPERATIONS
// =============================================================================
/**
* Create new storage pool
* @returns {Promise<void>}
*/
async function createPool() {
console.log('โ Opening Create Storage Pool dialog');
// Create storage pool creation modal
const modalHtml = `
<div id="createPoolModal" class="modal" style="display: block;">
<div class="modal-content" style="max-width: 600px;">
<div class="modal-header">
<h3><i class="fas fa-plus"></i> Create Storage Pool</h3>
<button class="close-btn" onclick="closeCreatePoolModal()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-body">
<form id="createPoolForm">
<div class="form-group">
<label>Pool Name:</label>
<input type="text" id="poolName" required placeholder="Enter pool name">
</div>
<div class="form-group">
<label>Pool Type:</label>
<select id="poolType" required>
<option value="btrfs">BTRFS (Recommended)</option>
<option value="xfs">XFS</option>
<option value="ext4">EXT4</option>
</select>
</div>
<div class="form-group">
<label>Device/Path:</label>
<input type="text" id="poolDevice" required placeholder="/dev/sdb or /path/to/directory">
</div>
</form>
</div>
<div class="modal-footer">
<button onclick="closeCreatePoolModal()" class="btn btn-secondary">Cancel</button>
<button onclick="submitCreatePool()" class="btn btn-primary">Create Pool</button>
</div>
</div>
</div>
`;
// Add modal to DOM
document.body.insertAdjacentHTML('beforeend', modalHtml);
document.body.classList.add('modal-open');
// Add modal functions
window.closeCreatePoolModal = () => {
const modal = document.getElementById('createPoolModal');
if (modal) {
modal.remove();
document.body.classList.remove('modal-open');
}
delete window.closeCreatePoolModal;
delete window.submitCreatePool;
};
window.submitCreatePool = async () => {
const name = document.getElementById('poolName').value;
const type = document.getElementById('poolType').value;
const device = document.getElementById('poolDevice').value;
if (!name || !type || !device) {
PersistenceCore.showNotification('Please fill in all fields', 'error');
return;
}
try {
await PersistenceCore.apiPost('/storage/pools', {
name,
type,
device
});
PersistenceCore.showNotification(`Storage pool "${name}" created successfully`, 'success');
window.closeCreatePoolModal();
await refreshStorage();
} catch (error) {
console.error('โ Failed to create storage pool:', error);
PersistenceCore.showNotification('Failed to create storage pool', 'error');
}
};
}
/**
* Import existing storage pool
* @returns {Promise<void>}
*/
async function importPool() {
console.log('๐ฅ Opening Import Storage Pool dialog');
PersistenceCore.showNotification('๐ง Import Pool feature - In development', 'info');
// TODO: Implement pool import functionality
// - Scan for existing pools
// - Import configuration
// - Validate pool integrity
}
/**
* Manage storage pool
* @param {string} poolName - Pool name
* @returns {Promise<void>}
*/
async function managePool(poolName) {
console.log(`โ๏ธ Managing storage pool: ${poolName}`);
const pool = storageState.storagePools.find(p => p.name === poolName);
if (!pool) {
PersistenceCore.showNotification('Storage pool not found', 'error');
return;
}
// Create pool management modal
const modalHtml = `
<div id="managePoolModal" class="modal" style="display: block;">
<div class="modal-content" style="max-width: 700px;">
<div class="modal-header">
<h3><i class="fas fa-cog"></i> Manage Pool - ${poolName}</h3>
<button class="close-btn" onclick="closeManagePoolModal()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-body">
<div class="pool-info">
<h4>Pool Information</h4>
<p><strong>Name:</strong> ${pool.name}</p>
<p><strong>Type:</strong> ${pool.type}</p>
<p><strong>Status:</strong> <span class="status-${pool.status}">${pool.status}</span></p>
<p><strong>Size:</strong> ${pool.size}</p>
<p><strong>Used:</strong> ${pool.used} (${pool.usedPercent}%)</p>
<p><strong>Available:</strong> ${pool.available}</p>
<p><strong>Mount Point:</strong> ${pool.mountPoint || 'N/A'}</p>
</div>
<div class="pool-actions">
<h4>Actions</h4>
<button class="btn btn-primary" onclick="scrubPool('${poolName}')">
<i class="fas fa-broom"></i> Scrub Pool
</button>
<button class="btn btn-secondary" onclick="repairPool('${poolName}')">
<i class="fas fa-wrench"></i> Repair Pool
</button>
<button class="btn btn-warning" onclick="exportPool('${poolName}')">
<i class="fas fa-download"></i> Export Pool
</button>
</div>
</div>
<div class="modal-footer">
<button onclick="closeManagePoolModal()" class="btn btn-secondary">Close</button>
</div>
</div>
</div>
`;
// Add modal to DOM
document.body.insertAdjacentHTML('beforeend', modalHtml);
document.body.classList.add('modal-open');
// Add modal functions
window.closeManagePoolModal = () => {
const modal = document.getElementById('managePoolModal');
if (modal) {
modal.remove();
document.body.classList.remove('modal-open');
}
delete window.closeManagePoolModal;
delete window.scrubPool;
delete window.repairPool;
delete window.exportPool;
};
window.scrubPool = async (name) => {
PersistenceCore.showNotification(`๐งน Starting scrub for pool "${name}"`, 'info');
// TODO: Implement pool scrub
};
window.repairPool = async (name) => {
PersistenceCore.showNotification(`๐ง Starting repair for pool "${name}"`, 'info');
// TODO: Implement pool repair
};
window.exportPool = async (name) => {
PersistenceCore.showNotification(`๐ค Exporting pool "${name}"`, 'info');
// TODO: Implement pool export
};
}
/**
* Create snapshot of storage pool
* @param {string} poolName - Pool name
* @returns {Promise<void>}
*/
async function snapshotPool(poolName) {
console.log(`๐ธ Creating snapshot for pool: ${poolName}`);
try {
const snapshotName = `${poolName}-${Date.now()}`;
await PersistenceCore.apiPost(`/storage/pools/${poolName}/snapshots`, {
name: snapshotName,
description: `Snapshot of ${poolName} created from UI`
});
PersistenceCore.showNotification(`Snapshot "${snapshotName}" created successfully`, 'success');
} catch (error) {
console.error('โ Failed to create pool snapshot:', error);
PersistenceCore.showNotification('Failed to create pool snapshot', 'error');
}
}
// =============================================================================
// STORAGE MONITORING
// =============================================================================
/**
* Start storage monitoring with automatic refresh
*/
function startStorageMonitoring() {
console.log('๐พ Starting storage monitoring...');
// Clear existing interval
if (storageState.refreshInterval) {
clearInterval(storageState.refreshInterval);
}
// Set up refresh interval
storageState.refreshInterval = setInterval(async () => {
if (!storageState.isLoading) {
await refreshStorage();
}
}, PersistenceCore.config.REFRESH_INTERVALS.STORAGE_DATA);
// Initial refresh
refreshStorage();
}
/**
* Stop storage monitoring
*/
function stopStorageMonitoring() {
console.log('โน๏ธ Stopping storage monitoring...');
if (storageState.refreshInterval) {
clearInterval(storageState.refreshInterval);
storageState.refreshInterval = null;
}
}
// =============================================================================
// INITIALIZATION
// =============================================================================
/**
* Initialize storage module
*/
function initializeStorageModule() {
console.log('๐พ Initializing storage management module...');
// Start monitoring if authenticated
if (PersistenceAuth.state.isAuthenticated) {
startStorageMonitoring();
}
// Listen for authentication changes
window.addEventListener('auth-state-changed', (event) => {
if (event.detail.isAuthenticated) {
startStorageMonitoring();
} else {
stopStorageMonitoring();
}
});
}
// Initialize on DOM ready
document.addEventListener('DOMContentLoaded', initializeStorageModule);
// =============================================================================
// EXPORT FOR MODULE SYSTEM
// =============================================================================
window.PersistenceStorage = {
state: storageState,
refreshStorage,
getStorageStats,
createPool,
importPool,
managePool,
snapshotPool,
startStorageMonitoring,
stopStorageMonitoring,
initializeStorageModule
};
console.log('โ
PersistenceOS Storage Management Module loaded');