feat: add scheduled daily backup, stored backup listing and download

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
GuavaPopper
2026-06-04 14:51:07 +07:00
parent 1535c75138
commit f77fc725a8
6 changed files with 146 additions and 2 deletions
+42 -1
View File
@@ -126,6 +126,30 @@
<tbody id="bodyUsers"></tbody>
</table>
</div>
<!-- Daftar Backup Terjadwal -->
<div class="bg-white rounded-2xl shadow-sm border border-base-200 mt-8">
<div class="p-4 border-b border-base-200 flex items-center justify-between">
<h2 class="font-bold text-base flex items-center gap-2">
<i class="fa-solid fa-clock-rotate-left text-success"></i> Backup Tersimpan
</h2>
<button onclick="loadBackupList()" class="btn btn-xs btn-ghost">
<i class="fa-solid fa-rotate-right"></i> Refresh
</button>
</div>
<div class="overflow-x-auto">
<table class="table table-sm w-full">
<thead>
<tr class="text-xs uppercase opacity-50">
<th>File</th><th>Ukuran</th><th>Waktu</th><th></th>
</tr>
</thead>
<tbody id="bodyBackupList">
<tr><td colspan="4" class="text-center py-6 opacity-30 italic">Memuat...</td></tr>
</tbody>
</table>
</div>
</div>
</main>
<!-- ═══ MODALS ═══════════════════════════════════════════════════════════ -->
@@ -259,7 +283,24 @@
return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
}
document.addEventListener('DOMContentLoaded', loadUsers);
document.addEventListener('DOMContentLoaded', () => { loadUsers(); loadBackupList(); });
async function loadBackupList() {
try {
const res = await fetch('{{ url('/api/admin/backup/list') }}', { headers: { 'X-CSRF-TOKEN': csrfToken } });
const list = await res.json();
document.getElementById('bodyBackupList').innerHTML = list.length
? list.map(b => `<tr>
<td class="font-mono text-xs">${esc(b.name)}</td>
<td class="text-xs opacity-60">${esc(String(b.size_kb))} KB</td>
<td class="text-xs opacity-60">${esc(b.created_at)}</td>
<td><a href="{{ url('/api/admin/backup/download') }}/${esc(b.name)}" class="btn btn-xs btn-ghost" title="Unduh"><i class="fa-solid fa-download"></i></a></td>
</tr>`).join('')
: '<tr><td colspan="4" class="text-center py-6 opacity-30 italic">Belum ada backup tersimpan</td></tr>';
} catch (e) {
showToast('Gagal memuat daftar backup', 'error');
}
}
async function loadUsers() {
try {