File network-module.js of Package PersistenceOS

/**
 * PersistenceOS Network Management Module
 * Handles network interface management, configuration, and network-related functionality
 * 
 * Depends on: core-utils.js, auth-module.js
 */

// =============================================================================
// NETWORK STATE MANAGEMENT
// =============================================================================

const networkState = {
    networkInterfaces: [],
    networkConnections: [],
    networkRoutes: [],
    firewallStatus: {
        firewalld: { active: false, default_zone: '', zones: [] },
        nftables: { active: false, rules_count: 0 }
    },
    dnsConfig: {
        nameservers: [],
        search_domains: [],
        resolv_conf: ''
    },
    networkSettings: {},
    selectedInterface: null,
    connectionForm: {
        name: '',
        type: 'ethernet',
        interface: '',
        ipv4: {
            method: 'auto',
            address: '',
            netmask: '',
            gateway: ''
        },
        dns: {
            servers: [],
            search: []
        }
    },
    networkStats: {
        totalInterfaces: 0,
        activeInterfaces: 0,
        totalConnections: 0,
        activeConnections: 0
    },
    networkLoading: false,
    interfaceSearchTerm: '',
    refreshInterval: null,
    lastUpdate: null
};

// =============================================================================
// NETWORK DATA FUNCTIONS
// =============================================================================

/**
 * Refresh network data from API
 * @returns {Promise<void>}
 */
async function refreshNetworkData() {
    networkState.networkLoading = true;
    console.log('🔄 Refreshing network data...');
    
    try {
        // Fetch network interfaces
        const interfacesData = await PersistenceCore.apiGet('/network/interfaces');
        console.log('✅ Network interfaces loaded:', interfacesData.interfaces?.length || 0);
        
        networkState.networkInterfaces = interfacesData.interfaces || [];
        
        // Fetch network routes
        const routesData = await PersistenceCore.apiGet('/network/routes');
        console.log('✅ Network routes loaded:', routesData.routes?.length || 0);
        
        networkState.networkRoutes = routesData.routes || [];
        
        // Fetch firewall status
        const firewallData = await PersistenceCore.apiGet('/network/firewall/status');
        console.log('✅ Firewall status loaded:', firewallData);
        
        networkState.firewallStatus = firewallData || networkState.firewallStatus;
        
        // Fetch DNS configuration
        const dnsData = await PersistenceCore.apiGet('/network/dns');
        console.log('✅ DNS configuration loaded:', dnsData);
        
        networkState.dnsConfig = dnsData || networkState.dnsConfig;
        
        // Update network statistics
        updateNetworkStats();
        
        networkState.lastUpdate = new Date();
        
        // Trigger custom event for Vue.js reactivity
        window.dispatchEvent(new CustomEvent('network-data-updated', {
            detail: {
                interfaces: networkState.networkInterfaces,
                routes: networkState.networkRoutes,
                firewall: networkState.firewallStatus,
                dns: networkState.dnsConfig,
                stats: networkState.networkStats
            }
        }));
        
        PersistenceCore.showNotification('✅ Network data refreshed successfully', 'success');
        
    } catch (error) {
        console.error('❌ Error refreshing network data:', error);
        PersistenceCore.showNotification('❌ Error refreshing network data', 'error');
    } finally {
        networkState.networkLoading = false;
    }
}

/**
 * Update network statistics
 */
function updateNetworkStats() {
    const interfaces = networkState.networkInterfaces;
    
    networkState.networkStats = {
        totalInterfaces: interfaces.length,
        activeInterfaces: interfaces.filter(iface => iface.state === 'up').length,
        totalConnections: networkState.networkConnections.length,
        activeConnections: networkState.networkConnections.filter(conn => conn.active).length
    };
}

/**
 * Get filtered interfaces based on search term
 * @returns {Array}
 */
function getFilteredInterfaces() {
    if (!networkState.interfaceSearchTerm) {
        return networkState.networkInterfaces;
    }
    
    const searchTerm = networkState.interfaceSearchTerm.toLowerCase();
    return networkState.networkInterfaces.filter(iface =>
        iface.name.toLowerCase().includes(searchTerm) ||
        iface.type.toLowerCase().includes(searchTerm)
    );
}

// =============================================================================
// NETWORK INTERFACE OPERATIONS
// =============================================================================

/**
 * Configure network interface
 * @param {string} interfaceName - Interface name
 * @returns {Promise<void>}
 */
async function configureInterface(interfaceName) {
    console.log(`âš™ī¸ Configuring interface: ${interfaceName}`);
    
    const iface = networkState.networkInterfaces.find(i => i.name === interfaceName);
    if (!iface) {
        PersistenceCore.showNotification('Interface not found', 'error');
        return;
    }
    
    // Create interface configuration modal
    const modalHtml = `
        <div id="configureInterfaceModal" class="modal" style="display: block;">
            <div class="modal-content" style="max-width: 600px;">
                <div class="modal-header">
                    <h3><i class="fas fa-cog"></i> Configure Interface - ${interfaceName}</h3>
                    <button class="close-btn" onclick="closeConfigureInterfaceModal()">
                        <i class="fas fa-times"></i>
                    </button>
                </div>
                <div class="modal-body">
                    <form id="interfaceConfigForm">
                        <div class="form-group">
                            <label>Interface Name:</label>
                            <input type="text" value="${iface.name}" readonly>
                        </div>
                        <div class="form-group">
                            <label>Configuration Method:</label>
                            <select id="configMethod">
                                <option value="auto">Automatic (DHCP)</option>
                                <option value="manual">Manual (Static IP)</option>
                            </select>
                        </div>
                        <div id="manualConfig" style="display: none;">
                            <div class="form-group">
                                <label>IP Address:</label>
                                <input type="text" id="ipAddress" placeholder="192.168.1.100">
                            </div>
                            <div class="form-group">
                                <label>Subnet Mask:</label>
                                <input type="text" id="subnetMask" placeholder="255.255.255.0">
                            </div>
                            <div class="form-group">
                                <label>Gateway:</label>
                                <input type="text" id="gateway" placeholder="192.168.1.1">
                            </div>
                        </div>
                    </form>
                </div>
                <div class="modal-footer">
                    <button onclick="closeConfigureInterfaceModal()" class="btn btn-secondary">Cancel</button>
                    <button onclick="applyInterfaceConfig('${interfaceName}')" class="btn btn-primary">Apply</button>
                </div>
            </div>
        </div>
    `;
    
    // Add modal to DOM
    document.body.insertAdjacentHTML('beforeend', modalHtml);
    document.body.classList.add('modal-open');
    
    // Add event listeners
    document.getElementById('configMethod').addEventListener('change', (e) => {
        const manualConfig = document.getElementById('manualConfig');
        manualConfig.style.display = e.target.value === 'manual' ? 'block' : 'none';
    });
    
    // Add modal functions
    window.closeConfigureInterfaceModal = () => {
        const modal = document.getElementById('configureInterfaceModal');
        if (modal) {
            modal.remove();
            document.body.classList.remove('modal-open');
        }
        delete window.closeConfigureInterfaceModal;
        delete window.applyInterfaceConfig;
    };
    
    window.applyInterfaceConfig = async (name) => {
        const method = document.getElementById('configMethod').value;
        const config = { method };
        
        if (method === 'manual') {
            config.ipAddress = document.getElementById('ipAddress').value;
            config.subnetMask = document.getElementById('subnetMask').value;
            config.gateway = document.getElementById('gateway').value;
            
            if (!config.ipAddress || !config.subnetMask) {
                PersistenceCore.showNotification('Please fill in required fields', 'error');
                return;
            }
        }
        
        try {
            await PersistenceCore.apiPost(`/network/interfaces/${name}/configure`, config);
            PersistenceCore.showNotification(`Interface "${name}" configured successfully`, 'success');
            window.closeConfigureInterfaceModal();
            await refreshNetworkData();
        } catch (error) {
            console.error('❌ Failed to configure interface:', error);
            PersistenceCore.showNotification('Failed to configure interface', 'error');
        }
    };
}

/**
 * Toggle interface state (up/down)
 * @param {string} interfaceName - Interface name
 * @returns {Promise<void>}
 */
async function toggleInterface(interfaceName) {
    console.log(`🔄 Toggling interface: ${interfaceName}`);
    
    const iface = networkState.networkInterfaces.find(i => i.name === interfaceName);
    if (!iface) {
        PersistenceCore.showNotification('Interface not found', 'error');
        return;
    }
    
    const action = iface.state === 'up' ? 'down' : 'up';
    
    try {
        await PersistenceCore.apiPost(`/network/interfaces/${interfaceName}/${action}`);
        PersistenceCore.showNotification(`Interface "${interfaceName}" ${action === 'up' ? 'enabled' : 'disabled'}`, 'success');
        await refreshNetworkData();
    } catch (error) {
        console.error(`❌ Failed to ${action} interface:`, error);
        PersistenceCore.showNotification(`Failed to ${action} interface`, 'error');
    }
}

/**
 * Show interface details
 * @param {string} interfaceName - Interface name
 */
function showInterfaceDetails(interfaceName) {
    console.log(`â„šī¸ Showing details for interface: ${interfaceName}`);
    
    const iface = networkState.networkInterfaces.find(i => i.name === interfaceName);
    if (!iface) {
        PersistenceCore.showNotification('Interface not found', 'error');
        return;
    }
    
    // Create interface details modal
    const modalHtml = `
        <div id="interfaceDetailsModal" class="modal" style="display: block;">
            <div class="modal-content" style="max-width: 600px;">
                <div class="modal-header">
                    <h3><i class="fas fa-info-circle"></i> Interface Details - ${interfaceName}</h3>
                    <button class="close-btn" onclick="closeInterfaceDetailsModal()">
                        <i class="fas fa-times"></i>
                    </button>
                </div>
                <div class="modal-body">
                    <div class="interface-details">
                        <h4>Basic Information</h4>
                        <p><strong>Name:</strong> ${iface.name}</p>
                        <p><strong>Type:</strong> ${iface.type}</p>
                        <p><strong>State:</strong> <span class="status-${iface.state}">${iface.state.toUpperCase()}</span></p>
                        <p><strong>MAC Address:</strong> ${iface.mac_address || 'N/A'}</p>
                        <p><strong>MTU:</strong> ${iface.mtu || 'N/A'}</p>
                        
                        <h4>IP Configuration</h4>
                        ${iface.ip_addresses && iface.ip_addresses.length > 0 ? 
                            iface.ip_addresses.map(ip => 
                                `<p><strong>${ip.family.toUpperCase()}:</strong> ${ip.address}/${ip.prefix}</p>`
                            ).join('') : 
                            '<p>No IP addresses assigned</p>'
                        }
                        
                        <h4>Statistics</h4>
                        <p><strong>RX Bytes:</strong> ${PersistenceCore.formatBytes(iface.rx_bytes || 0)}</p>
                        <p><strong>TX Bytes:</strong> ${PersistenceCore.formatBytes(iface.tx_bytes || 0)}</p>
                        <p><strong>RX Packets:</strong> ${iface.rx_packets || 0}</p>
                        <p><strong>TX Packets:</strong> ${iface.tx_packets || 0}</p>
                    </div>
                </div>
                <div class="modal-footer">
                    <button onclick="closeInterfaceDetailsModal()" 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 function
    window.closeInterfaceDetailsModal = () => {
        const modal = document.getElementById('interfaceDetailsModal');
        if (modal) {
            modal.remove();
            document.body.classList.remove('modal-open');
        }
        delete window.closeInterfaceDetailsModal;
    };
}

// =============================================================================
// NETWORK MONITORING
// =============================================================================

/**
 * Start network monitoring with automatic refresh
 */
function startNetworkMonitoring() {
    console.log('🌐 Starting network monitoring...');
    
    // Clear existing interval
    if (networkState.refreshInterval) {
        clearInterval(networkState.refreshInterval);
    }
    
    // Set up refresh interval
    networkState.refreshInterval = setInterval(async () => {
        if (!networkState.networkLoading) {
            await refreshNetworkData();
        }
    }, PersistenceCore.config.REFRESH_INTERVALS.NETWORK_DATA);
    
    // Initial refresh
    refreshNetworkData();
}

/**
 * Stop network monitoring
 */
function stopNetworkMonitoring() {
    console.log('âšī¸ Stopping network monitoring...');
    
    if (networkState.refreshInterval) {
        clearInterval(networkState.refreshInterval);
        networkState.refreshInterval = null;
    }
}

// =============================================================================
// INITIALIZATION
// =============================================================================

/**
 * Initialize network module
 */
function initializeNetworkModule() {
    console.log('🌐 Initializing network management module...');
    
    // Start monitoring if authenticated
    if (PersistenceAuth.state.isAuthenticated) {
        startNetworkMonitoring();
    }
    
    // Listen for authentication changes
    window.addEventListener('auth-state-changed', (event) => {
        if (event.detail.isAuthenticated) {
            startNetworkMonitoring();
        } else {
            stopNetworkMonitoring();
        }
    });
}

// Initialize on DOM ready
document.addEventListener('DOMContentLoaded', initializeNetworkModule);

// =============================================================================
// EXPORT FOR MODULE SYSTEM
// =============================================================================

window.PersistenceNetwork = {
    state: networkState,
    refreshNetworkData,
    updateNetworkStats,
    getFilteredInterfaces,
    configureInterface,
    toggleInterface,
    showInterfaceDetails,
    startNetworkMonitoring,
    stopNetworkMonitoring,
    initializeNetworkModule
};

console.log('✅ PersistenceOS Network Management Module loaded');
openSUSE Build Service is sponsored by