Files
Homepage/js/export-import.js

196 lines
5.7 KiB
JavaScript

/**
* 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 = '<?xml version="1.0" encoding="utf-8"?>\n';
xml += `<services${config.tailscaleIp ? ` tailscale-ip="${config.tailscaleIp}"` : ''}>\n`;
config.groups.forEach(group => {
xml += ` <group name="${escapeXml(group.name)}">\n`;
group.services.forEach(service => {
xml += ' <service';
Object.entries(service).forEach(([key, value]) => {
xml += ` ${key}="${escapeXml(value)}"`;
});
xml += ' />\n';
});
xml += ' </group>\n';
});
xml += '</services>\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 = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&apos;'
};
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
};