/** * Backup Manager module for ObsiGate. * * Provides a full-page view to browse, inspect, and delete all backups * across all vaults. Accessible from the backup/diff viewer. */ import { api } from './auth.js'; import { showToast } from './ui.js'; import { safeCreateIcons } from './utils.js'; var state = { backups: [], filter: '' }; export function openBackupManager() { var area = document.getElementById('content-area'); if (!area) return; area.innerHTML = ''; var container = document.createElement('div'); container.className = 'backup-manager'; container.id = 'backup-manager'; container.innerHTML = '
' + '

Gestion des backups

' + '
' + '' + '' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
'; area.appendChild(container); safeCreateIcons(); document.getElementById('bm-filter').addEventListener('input', function() { state.filter = this.value.toLowerCase(); render(); }); document.getElementById('bm-refresh').addEventListener('click', load); load(); } async function load() { var list = document.getElementById('bm-list'); list.innerHTML = '
'; try { var data = await api('/api/backups'); state.backups = data.backups || []; updateStats(data); render(); } catch (err) { list.innerHTML = '
Erreur: ' + escapeHtml(err.message || 'Inconnue') + '
'; } } function updateStats(data) { var stats = document.getElementById('bm-stats'); if (!stats) return; var totalSize = formatSize(data.total_size_bytes || 0); stats.innerHTML = '' + data.total + ' backups' + '' + totalSize + ' total'; } function render() { var list = document.getElementById('bm-list'); if (!list) return; var filtered = state.backups; if (state.filter) { filtered = state.backups.filter(function(b) { return b.file.toLowerCase().indexOf(state.filter) !== -1 || b.vault.toLowerCase().indexOf(state.filter) !== -1; }); } if (filtered.length === 0) { list.innerHTML = '
Aucun backup trouve.
'; return; } // Group by vault var grouped = {}; filtered.forEach(function(b) { if (!grouped[b.vault]) grouped[b.vault] = []; grouped[b.vault].push(b); }); var html = ''; var vaultNames = Object.keys(grouped).sort(); vaultNames.forEach(function(vault) { html += '
'; html += '

' + escapeHtml(vault) + ' (' + grouped[vault].length + ')

'; // Group by file var byFile = {}; grouped[vault].forEach(function(b) { if (!byFile[b.file]) byFile[b.file] = []; byFile[b.file].push(b); }); var fileNames = Object.keys(byFile).sort(); fileNames.forEach(function(file) { var backups = byFile[file]; backups.sort(function(a, b) { return b.timestamp - a.timestamp; }); var totalFileSize = backups.reduce(function(s, b) { return s + b.size; }, 0); html += '
'; html += '
'; html += ' ' + escapeHtml(file) + ''; html += '' + backups.length + ' versions · ' + formatSize(totalFileSize) + ''; html += ''; html += '
'; html += '
'; backups.forEach(function(b) { var d = new Date(b.timestamp * 1000); var ds = d.getFullYear() + '-' + pad(d.getMonth()+1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()); html += '
'; html += '' + ds + ''; html += '' + formatSize(b.size) + ''; html += ''; html += '
'; }); html += '
'; html += '
'; }); html += '
'; }); list.innerHTML = html; safeCreateIcons(); // Bind delete buttons list.querySelectorAll('.bm-backup-delete').forEach(function(btn) { btn.addEventListener('click', function() { if (confirm('Supprimer ce backup ?')) { deleteBackups([btn.dataset.path]); } }); }); // Bind purge buttons list.querySelectorAll('.bm-file-purge').forEach(function(btn) { btn.addEventListener('click', function() { if (confirm('Supprimer TOUS les backups de "' + btn.dataset.file + '" ?')) { purgeFile(btn.dataset.vault, btn.dataset.file); } }); }); } async function deleteBackups(paths) { try { var resp = await api('/api/backups/delete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ paths: paths }), }); showToast(resp.deleted + ' backup(s) supprime(s)', 'success'); load(); } catch (err) { showToast('Erreur: ' + err.message, 'error'); } } async function purgeFile(vault, file) { try { var resp = await api('/api/backups/purge', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ vault: vault, file: file }), }); showToast(resp.deleted + ' backup(s) supprime(s)', 'success'); load(); } catch (err) { showToast('Erreur: ' + err.message, 'error'); } } function formatSize(bytes) { if (bytes < 1024) return bytes + ' o'; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' Ko'; return (bytes / (1024 * 1024)).toFixed(1) + ' Mo'; } function pad(n) { return String(n).padStart(2, '0'); } function escapeHtml(str) { var div = document.createElement('div'); div.textContent = str; return div.innerHTML; }