diff --git a/public/app.js b/public/app.js
index 54b61bb..8a9fbf3 100644
--- a/public/app.js
+++ b/public/app.js
@@ -49,6 +49,9 @@ function navigateTo(viewName) {
if (viewName === 'servers') {
renderServers();
}
+ if (viewName === 'runs') {
+ loadAllRuns();
+ }
if (viewName === 'backups') {
renderBackupsView();
}
@@ -99,16 +102,27 @@ async function renderBackupsView() {
- | Data | Mode | Status | Containers |
+ | Data | Mode | Status | Containers | Actions |
- ${backups.length ? backups.map((b) => `
+ ${backups.length ? backups.map((b) => {
+ const hasRestorable = (b.containers || []).some((c) => c.status === 'ok');
+ return `
| ${escapeHtml(new Date(b.createdAt).toLocaleString('pt-BR'))} |
${escapeHtml(b.mode || '—')} |
${escapeHtml(b.status)} |
${escapeHtml((b.containers || []).map((c) => c.containerName).join(', '))} |
+
+
+ |
- `).join('') : '| Nenhum backup realizado. |
'}
+ `}).join('') : '| Nenhum backup realizado. |
'}
@@ -127,16 +141,11 @@ async function updateDashboard() {
.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
const lastBackupEl = document.querySelector('#lastBackupTime');
if (lastBackupEl) {
- lastBackupEl.textContent = successful.length
- ? new Date(successful[0].createdAt).toLocaleString('pt-BR')
- : '—';
+ lastBackupEl.textContent = String(successful.length);
}
- // Failed in last 24h
- const cutoff = Date.now() - 24 * 60 * 60 * 1000;
- const failed = allBackups.filter(
- (b) => b.status === 'error' && new Date(b.createdAt).getTime() >= cutoff,
- );
+ // Failed total
+ const failed = allBackups.filter((b) => b.status === 'error');
const failedEl = document.querySelector('#failedCount');
if (failedEl) failedEl.textContent = String(failed.length);
@@ -191,6 +200,54 @@ function formatBytes(bytes) {
return bytes + ' B';
}
+async function loadAllRuns() {
+ const tbody = document.querySelector('#allRunsBody');
+ if (!tbody) return;
+ tbody.innerHTML = '| Carregando... |
';
+
+ if (!state.profiles.length) {
+ await loadProfiles();
+ }
+
+ if (!state.profiles.length) {
+ tbody.innerHTML = '| Nenhum profile encontrado. |
';
+ return;
+ }
+
+ const allBackups = (await Promise.all(
+ state.profiles.map((p) => api(`/api/profiles/${p.id}/backups`)),
+ )).flat();
+
+ const sorted = [...allBackups].sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
+
+ if (!sorted.length) {
+ tbody.innerHTML = '| Nenhum run encontrado. |
';
+ return;
+ }
+
+ tbody.innerHTML = sorted.map((b, index) => {
+ const profile = state.profiles.find((p) => p.id === b.profileId);
+ const profileName = profile ? profile.name : (b.profileName || b.profileId || '—');
+ const containers = (b.containers || []).map((c) => escapeHtml(c.containerName || c.containerId?.slice(0, 12) || '?')).join(', ');
+ const fileCount = (b.containers || []).reduce((sum, c) => sum + (c.fileCount || 0), 0);
+ const size = (b.containers || []).reduce((sum, c) => sum + (c.archiveSize || 0), 0);
+ const sizeStr = size > 0 ? formatBytes(size) : '—';
+ const statusLabel = b.status === 'ok' ? 'Completed' : b.status === 'partial' ? 'Partial' : 'Error';
+ return `
+
+ | ${index + 1} |
+ ${escapeHtml(profileName)} |
+ ${escapeHtml(b.mode || '—')} |
+ ${escapeHtml(statusLabel)} |
+ ${containers || '—'} |
+ ${fileCount || '—'} |
+ ${sizeStr} |
+ ${escapeHtml(new Date(b.createdAt).toLocaleString('pt-BR'))} |
+
+ `;
+ }).join('');
+}
+
async function api(path, options = {}) {
const response = await fetch(path, {
headers: {
@@ -925,5 +982,7 @@ document.querySelector('#refreshContainers').addEventListener('click', init);
document.querySelector('#reloadProfiles').addEventListener('click', loadProfiles);
document.querySelector('#clearForm').addEventListener('click', resetForm);
elements.profilesList.addEventListener('click', handleProfileAction);
+document.querySelector('#backupsViewList').addEventListener('click', handleProfileAction);
+document.querySelector('#refreshRuns')?.addEventListener('click', () => loadAllRuns());
init();
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index a26c1dd..4f98f7d 100644
--- a/public/index.html
+++ b/public/index.html
@@ -106,9 +106,9 @@
- Last Backup
- —
- Latest successful run
+ Successful
+ 0
+ Total com sucesso
@@ -117,9 +117,9 @@
- Failed (24h)
+ Failed
0
- Needs attention
+ Total com falha