🚀 Auto-deploy: BrainKP atualizado em 22/06/2026 18:58:20
This commit is contained in:
+66
-33
@@ -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 = '<span class="loading" style="width:10px;height:10px;margin-right:4px;border-width:2px"></span> 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 => `
|
||||
<div class="card" onclick="switchTab('${c.id}')">
|
||||
<div class="card" onclick="switchTab('${c.id}')" style="${c.running ? 'border-color:var(--accent)' : ''}">
|
||||
<div class="card-icon">${c.icon}</div>
|
||||
<div class="card-name">${c.name}</div>
|
||||
<div class="card-meta">
|
||||
<span>${c.desc}</span>
|
||||
<span style="font-weight:600;color:var(--text);font-size:12px;margin:4px 0">${c.count} arquivo${c.count!==1?'s':''} de backup</span>
|
||||
<span>${c.meta}</span>
|
||||
<span>${c.size}</span>
|
||||
<span>Tamanho: ${c.size}</span>
|
||||
</div>
|
||||
<button class="run-btn" onclick="event.stopPropagation();runBackup('${c.id}')">▶ Executar Backup</button>
|
||||
${c.running
|
||||
? `<div style="margin-top:12px;text-align:center;font-size:11px;color:var(--accent);font-weight:bold"><span class="loading"></span> Sincronizando...</div>`
|
||||
: `<button class="run-btn" onclick="event.stopPropagation();runBackup('${c.id}')">▶ Executar Backup</button>`
|
||||
}
|
||||
</div>
|
||||
`).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) {
|
||||
<div class="backup-item">
|
||||
<div class="bi-left">
|
||||
<span class="bi-name">${item.name}</span>
|
||||
<span class="bi-date">${timeAgo(item.modified)}</span>
|
||||
<span class="bi-date">${formatDateTime(item.modified)}</span>
|
||||
</div>
|
||||
<div class="bi-right">
|
||||
<span class="bi-size">${formatSize(item.size)}</span>
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user