Full codebase including API server (32 modules + routes), dashboard frontend, DashCA certificate distribution, installer script, and deployment skills.
170 lines
6.0 KiB
JavaScript
170 lines
6.0 KiB
JavaScript
// ========== IMPORT/EXPORT FUNCTIONALITY ==========
|
|
(function() {
|
|
// Export dashboard configuration
|
|
async function exportDashboard() {
|
|
// Fetch themes from server (source of truth) with localStorage fallback
|
|
let userThemes = safeGetJSON('user-themes', {});
|
|
try {
|
|
const res = await secureFetch('/api/v1/themes');
|
|
const data = await res.json();
|
|
if (data.success && data.themes) userThemes = data.themes;
|
|
} catch (e) {}
|
|
|
|
const exportData = {
|
|
version: '1.0.0',
|
|
exportDate: new Date().toISOString(),
|
|
services: window.APPS || [],
|
|
customServices: safeGetJSON('custom-services', []),
|
|
customApps: safeGetJSON('custom-apps', []),
|
|
weatherZip: safeGet('weather-zip') || '',
|
|
theme: safeGet('theme') || 'dark',
|
|
userThemes: userThemes,
|
|
// Note: API tokens are intentionally NOT exported for security
|
|
};
|
|
|
|
const dataStr = JSON.stringify(exportData, null, 2);
|
|
const dataBlob = new Blob([dataStr], { type: 'application/json' });
|
|
const url = URL.createObjectURL(dataBlob);
|
|
|
|
const link = document.createElement('a');
|
|
link.href = url;
|
|
link.download = `dashcaddy-backup-${new Date().toISOString().split('T')[0]}.json`;
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
URL.revokeObjectURL(url);
|
|
|
|
showNotification('Dashboard exported successfully! Note: API tokens are not included for security reasons.', 'success');
|
|
}
|
|
|
|
// Import dashboard configuration
|
|
function importDashboard() {
|
|
const input = document.createElement('input');
|
|
input.type = 'file';
|
|
input.accept = 'application/json,.json';
|
|
|
|
input.onchange = async (e) => {
|
|
const file = e.target.files[0];
|
|
if (!file) return;
|
|
|
|
try {
|
|
const text = await file.text();
|
|
const importData = JSON.parse(text);
|
|
|
|
// Validate import data
|
|
if (!importData.version || !importData.services) {
|
|
throw new Error('Invalid dashboard backup file');
|
|
}
|
|
|
|
// Confirm import
|
|
const confirmed = confirm(
|
|
`Import dashboard configuration?\n\n` +
|
|
`Export Date: ${new Date(importData.exportDate).toLocaleString()}\n` +
|
|
`Services: ${importData.services.length}\n` +
|
|
`Custom Apps: ${(importData.customApps || []).length}\n\n` +
|
|
`⚠️ This will replace your current dashboard configuration.\n` +
|
|
`API tokens will need to be reconfigured.`
|
|
);
|
|
|
|
if (!confirmed) return;
|
|
|
|
// Import services to API
|
|
try {
|
|
const response = await secureFetch('/api/v1/services', {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(importData.services)
|
|
});
|
|
|
|
if (!response.ok) {
|
|
console.warn('Could not save services to API, saving locally only');
|
|
}
|
|
} catch (err) {
|
|
console.warn('API not available, saving locally only:', err);
|
|
}
|
|
|
|
// Import to localStorage
|
|
if (importData.customServices) {
|
|
safeSet('custom-services', JSON.stringify(importData.customServices));
|
|
}
|
|
if (importData.customApps) {
|
|
safeSet('custom-apps', JSON.stringify(importData.customApps));
|
|
}
|
|
if (importData.weatherZip) {
|
|
safeSet('weather-zip', importData.weatherZip);
|
|
}
|
|
if (importData.theme) {
|
|
safeSet('theme', importData.theme);
|
|
}
|
|
if (importData.userThemes && Object.keys(importData.userThemes).length) {
|
|
safeSet('user-themes', JSON.stringify(importData.userThemes));
|
|
// Push imported themes to server
|
|
Object.keys(importData.userThemes).forEach(function (slug) {
|
|
var t = importData.userThemes[slug];
|
|
var colors = {};
|
|
(window.THEME_PROPS || []).forEach(function (p) { if (t[p]) colors[p] = t[p]; });
|
|
secureFetch('/api/v1/themes/' + slug, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: t.name || slug, colors: colors })
|
|
}).catch(function () {});
|
|
});
|
|
}
|
|
|
|
// Update APPS array
|
|
window.APPS = importData.services;
|
|
|
|
showNotification('Dashboard imported successfully! The page will now reload.', 'success');
|
|
|
|
// Reload page to apply changes
|
|
window.location.reload();
|
|
|
|
} catch (err) {
|
|
showNotification(`Import failed: ${err.message}. Please check the file and try again.`, 'error');
|
|
console.error('Import error:', err);
|
|
}
|
|
};
|
|
|
|
input.click();
|
|
}
|
|
|
|
// Add event listeners for import/export buttons
|
|
document.getElementById('export-dashboard')?.addEventListener('click', exportDashboard);
|
|
document.getElementById('import-dashboard')?.addEventListener('click', importDashboard);
|
|
|
|
// Reload Caddy button handler
|
|
document.getElementById('reload-caddy-top')?.addEventListener('click', async () => {
|
|
const button = document.getElementById('reload-caddy-top');
|
|
const originalText = button.textContent;
|
|
|
|
try {
|
|
button.textContent = '⏳ Reloading...';
|
|
button.disabled = true;
|
|
|
|
const response = await secureFetch('/api/v1/caddy/reload', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (response.ok && result.success) {
|
|
button.textContent = '✅ Reloaded!';
|
|
setTimeout(() => {
|
|
button.textContent = originalText;
|
|
button.disabled = false;
|
|
}, 2000);
|
|
} else {
|
|
throw new Error(result.error || 'Reload failed');
|
|
}
|
|
} catch (error) {
|
|
button.textContent = '❌ Failed';
|
|
showNotification(`Failed to reload Caddy: ${error.message}`, 'error');
|
|
setTimeout(() => {
|
|
button.textContent = originalText;
|
|
button.disabled = false;
|
|
}, 2000);
|
|
}
|
|
});
|
|
})();
|