/** * export-import.js - Export and import service configurations */ // Export services configuration to JSON function exportConfiguration() { // Fetch services.xml and convert to JSON fetch('/services.xml') .then(response => response.text()) .then(xmlText => { const parser = new DOMParser(); const xmlDoc = parser.parseFromString(xmlText, 'text/xml'); const config = { version: '1.0', exportDate: new Date().toISOString(), tailscaleIp: xmlDoc.documentElement.getAttribute('tailscale-ip'), groups: [] }; // Parse groups const groups = xmlDoc.querySelectorAll('group'); groups.forEach(group => { const groupData = { name: group.getAttribute('name'), services: [] }; const services = group.querySelectorAll('service'); services.forEach(service => { const serviceData = {}; // Get all attributes Array.from(service.attributes).forEach(attr => { serviceData[attr.name] = attr.value; }); groupData.services.push(serviceData); }); config.groups.push(groupData); }); // Download JSON file const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `services-config-${new Date().toISOString().split('T')[0]}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showNotification('Configuration exported successfully!', 'success'); }) .catch(error => { console.error('Export error:', error); showNotification('Failed to export configuration', 'error'); }); } // Import services configuration from JSON function importConfiguration(file) { const reader = new FileReader(); reader.onload = (e) => { try { const config = JSON.parse(e.target.result); // Validate config structure if (!config.version || !config.groups) { throw new Error('Invalid configuration format'); } // Convert JSON back to XML let xml = '\n'; xml += `\n`; config.groups.forEach(group => { xml += ` \n`; group.services.forEach(service => { xml += ' { xml += ` ${key}="${escapeXml(value)}"`; }); xml += ' />\n'; }); xml += ' \n'; }); xml += '\n'; // Download the generated XML const blob = new Blob([xml], { type: 'text/xml' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'services.xml'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showNotification('Configuration imported! Download services.xml and replace the existing file.', 'success'); } catch (error) { console.error('Import error:', error); showNotification('Failed to import configuration: ' + error.message, 'error'); } }; reader.readAsText(file); } // Escape XML special characters function escapeXml(text) { const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, m => map[m]); } // Show notification function showNotification(message, type = 'info') { const notification = document.createElement('div'); notification.className = `notification notification-${type}`; notification.textContent = message; document.body.appendChild(notification); // Trigger animation setTimeout(() => notification.classList.add('show'), 10); // Remove after 3 seconds setTimeout(() => { notification.classList.remove('show'); setTimeout(() => notification.remove(), 300); }, 3000); } // Create import/export UI function createImportExportUI() { const container = document.createElement('div'); container.className = 'import-export-controls'; const exportBtn = document.createElement('button'); exportBtn.className = 'control-btn'; exportBtn.innerHTML = '📥 Export'; exportBtn.title = 'Export configuration to JSON'; exportBtn.addEventListener('click', exportConfiguration); const importBtn = document.createElement('button'); importBtn.className = 'control-btn'; importBtn.innerHTML = '📤 Import'; importBtn.title = 'Import configuration from JSON'; const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.json'; fileInput.style.display = 'none'; fileInput.addEventListener('change', (e) => { if (e.target.files.length > 0) { importConfiguration(e.target.files[0]); } }); importBtn.addEventListener('click', () => fileInput.click()); container.appendChild(exportBtn); container.appendChild(importBtn); container.appendChild(fileInput); return container; } // Initialize import/export functionality function initImportExport() { const header = document.querySelector('header'); if (header) { const ui = createImportExportUI(); header.appendChild(ui); } } // Export for use in other modules window.importExportModule = { init: initImportExport, exportConfiguration: exportConfiguration };