diff --git a/static/index.html b/static/index.html index 66f81c0..f728ff8 100644 --- a/static/index.html +++ b/static/index.html @@ -273,10 +273,21 @@ async function loadStatus() { // Last run badge const lr = d.log.last_run; const badge = document.getElementById('last-run'); + let isAnyRunning = false; + let runningScripts = []; + if (lr) { - const ok = !lr.has_error && lr.scripts.every(s => s.status === 'ok'); - badge.textContent = ok ? '✓ OK' : '⚠ Erro'; - badge.className = `badge ${ok ? 'green' : 'red'}`; + const isRunning = lr.scripts.some(s => s.status === 'running'); + if (isRunning) { + isAnyRunning = true; + runningScripts = lr.scripts.filter(s => s.status === 'running').map(s => s.name); + badge.innerHTML = ' Em andamento'; + badge.className = 'badge yellow'; + } else { + const ok = !lr.has_error && lr.scripts.every(s => s.status === 'ok'); + badge.textContent = ok ? '✓ Concluído' : '⚠ Erro'; + badge.className = `badge ${ok ? 'green' : 'red'}`; + } } else { badge.textContent = '—'; badge.className = 'badge yellow'; @@ -284,24 +295,47 @@ async function loadStatus() { // Cards const s = d.stats; + const isAppsRunning = runningScripts.includes('Backup Apps'); + const isBdRunning = runningScripts.includes('Backup BD'); + const isGitRunning = runningScripts.includes('Backup Git'); + const isReposRunning = runningScripts.includes('Backup repos Git'); + const cards = [ - {id:'apps', icon:'📦', name:'Apps', desc:`${s.apps.count} backup${s.apps.count!==1?'s':''}`, meta: s.apps.latest ? `Último: ${timeAgo(s.apps.latest)}` : '', size: formatSize(s.apps.size)}, - {id:'bd', icon:'🗄️', name:'Bancos', desc:`${s.bd.count} backup${s.bd.count!==1?'s':''}`, meta: s.bd.latest ? `Último: ${timeAgo(s.bd.latest)}` : '', size: formatSize(s.bd.size)}, - {id:'git', icon:'🐙', name:'Git Full', desc:`${s.git.count} backup${s.git.count!==1?'s':''}`, meta: s.git.latest ? `Último: ${timeAgo(s.git.latest)}` : '', size: formatSize(s.git.size)}, - {id:'repos', icon:'📁', name:'Repos', desc:`${s.repos.count} backup${s.repos.count!==1?'s':''}`, meta: s.repos.latest ? `Último: ${timeAgo(s.repos.latest)}` : '', size: formatSize(s.repos.size)}, + {id:'apps', icon:'📦', name:'Apps', count: s.apps.count, meta: s.apps.latest ? `Último: ${formatDateTime(s.apps.latest)}` : 'Nenhum backup', size: formatSize(s.apps.size), running: isAppsRunning}, + {id:'bd', icon:'🗄️', name:'Bancos', count: s.bd.count, meta: s.bd.latest ? `Último: ${formatDateTime(s.bd.latest)}` : 'Nenhum backup', size: formatSize(s.bd.size), running: isBdRunning}, + {id:'git', icon:'🐙', name:'Git Full', count: s.git.count, meta: s.git.latest ? `Último: ${formatDateTime(s.git.latest)}` : 'Nenhum backup', size: formatSize(s.git.size), running: isGitRunning}, + {id:'repos', icon:'📁', name:'Repos', count: s.repos.count, meta: s.repos.latest ? `Último: ${formatDateTime(s.repos.latest)}` : 'Nenhum backup', size: formatSize(s.repos.size), running: isReposRunning}, ]; + document.getElementById('cards').innerHTML = cards.map(c => ` -
+
${c.icon}
${c.name}
- ${c.desc} + ${c.count} arquivo${c.count!==1?'s':''} de backup ${c.meta} - ${c.size} + Tamanho: ${c.size}
- + ${c.running + ? `
Sincronizando...
` + : `` + }
`).join(''); + + // Poll faster if something is running + if (isAnyRunning) { + if (!window.pollIntervalFast) { + window.pollIntervalFast = setInterval(loadStatus, 2000); + } + } else { + if (window.pollIntervalFast) { + clearInterval(window.pollIntervalFast); + window.pollIntervalFast = null; + // Reload backups to show the newly finished one + loadBackups(); + } + } } catch(e) { console.error(e); } } @@ -327,7 +361,7 @@ function renderBackupList(items) {
${item.name} - ${timeAgo(item.modified)} + ${formatDateTime(item.modified)}
${formatSize(item.size)} @@ -404,7 +438,7 @@ async function runBackup(type) { const d = await res.json(); if (d.ok) { showToast(`${type} backup iniciado ✓`); - setTimeout(loadAll, 3000); + loadStatus(); } else { showToast(d.error, 'error'); } @@ -560,28 +594,27 @@ function formatSize(bytes) { return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } -function timeAgo(dateStr) { +function formatDateTime(dateStr) { if (!dateStr) return '—'; - // dateStr can be filename (20260622) or ModTime - const m = dateStr.match(/(\d{4})(\d{2})(\d{2})/); - if (m) { - const d = new Date(m[1], m[2]-1, m[3]); - const diff = Date.now() - d.getTime(); - const days = Math.floor(diff / 86400000); - if (days === 0) return 'Hoje'; - if (days === 1) return 'Ontem'; - return `há ${days} dia${days>1?'s':''}`; - } try { - const d = new Date(dateStr); - const diff = Date.now() - d.getTime(); - const mins = Math.floor(diff / 60000); - if (mins < 60) return `há ${mins}m`; - const hrs = Math.floor(mins / 60); - if (hrs < 24) return `há ${hrs}h`; - const days = Math.floor(hrs / 24); - if (days < 7) return `há ${days}d`; - return d.toLocaleDateString('pt-BR'); + // try to match YYYYMMDD_HHMMSS if it's from filename + const m = dateStr.match(/(\d{4})(\d{2})(\d{2})_?(\d{2})?(\d{2})?(\d{2})?/); + let d; + if (m && m[1] && m[2] && m[3] && dateStr.indexOf('-') === -1) { + d = new Date(m[1], m[2]-1, m[3], m[4]||0, m[5]||0, m[6]||0); + } else { + d = new Date(dateStr); + } + + if (isNaN(d.getTime())) return dateStr; + + const day = String(d.getDate()).padStart(2, '0'); + const month = String(d.getMonth() + 1).padStart(2, '0'); + const year = d.getFullYear(); + const hr = String(d.getHours()).padStart(2, '0'); + const min = String(d.getMinutes()).padStart(2, '0'); + + return `${day}/${month}/${year} às ${hr}:${min}`; } catch { return dateStr; } }