import { openBackupCreateModal } from "./backupCreateModal"; import { openBackupRestoreModal } from "./backupRestoreModal"; export function backupsList(propsArg = {}) { const props = store({ reset: null, }); const watchers = app.utils.extendStore(props, propsArg); const data = store({ canBackup: true, isLoading: false, isDownloading: {}, isDeleting: {}, backups: [], }); async function loadBackups() { data.isLoading = true; try { data.backups = await app.pb.backups.getFullList(); // sort backups DESC by their modified date data.backups.sort((a, b) => { if (a.modified < b.modified) { return 1; } if (a.modified > b.modified) { return -1; } return 0; }); data.isLoading = false; } catch (err) { if (!err.isAbort) { app.checkApiError(err); data.isLoading = false; } } } async function confirmBackupDelete(key) { app.modals.confirm(`Do you really want to delete ${key}?`, () => deleteBackup(key)); } async function deleteBackup(key) { if (data.isDeleting[key]) { return; } data.isDeleting[key] = true; try { await app.pb.backups.delete(key); loadBackups(); app.toasts.success(`Successfully deleted ${key}.`); } catch (err) { app.checkApiError(err); } delete data.isDeleting[key]; } async function loadCanBackup() { try { const health = await app.pb.health.check({ requestKey: null }); const oldCanBackup = data.canBackup; data.canBackup = health?.data?.canBackup || false; // reload backups list if (data.canBackup && oldCanBackup != data.canBackup) { loadBackups(); } } catch (err) { console.warn("failed to load canBackup checks", err); } } async function downloadBackup(key) { if (data.isDownloading[key]) { return; } data.isDownloading[key] = true; try { const token = await app.pb.files.getToken({ requestKey: null }); app.utils.download(app.pb.backups.getDownloadURL(token, key)); } catch (err) { app.checkApiError(err); } delete data.isDownloading[key]; } return t.div( { pbEvent: "backupsList", className: "list backups-list", onmount: (el) => { watchers.push(watch(() => props.reset, () => { loadBackups(); })); el._canBackupIntervalId = setInterval(() => { loadCanBackup(); }, 3500); }, onunmount: (el) => { clearInterval(el._canBackupIntervalId); watchers.forEach((w) => w?.unwatch()); }, }, t.div( { className: "list-content" }, t.div( { hidden: () => !data.isLoading || data.backups.length, className: "list-item", }, t.div({ className: "skeleton-loader" }), ), t.div( { hidden: () => data.isLoading || data.backups.length, className: () => "list-item", }, t.div({ className: "content block txt-hint" }, "No backups found."), ), () => { return data.backups.map((backup) => { return t.div( { className: () => `list-item ${data.isLoading ? "faded" : ""}` }, t.i({ className: "ri-folder-zip-line", ariaHidden: true }), t.div( { className: "content" }, t.span({ className: "backup-name txt-ellipsis", title: () => backup.key, textContent: () => backup.key, }), t.small( { className: "backup-size txt-hint txt-nowrap" }, "(", () => app.utils.formattedFileSize(backup.size), ")", ), ), t.nav( { hidden: () => data.isLoading, className: "actions autohide", }, t.button( { type: "button", ariaLabel: app.attrs.tooltip("Download"), className: () => `btn sm circle secondary transparent ${ data.isDownloading[backup.key] ? "loading" : "" }`, disabled: () => data.isDeleting[backup.key] || data.isDownloading[backup.key], onclick: () => downloadBackup(backup.key), }, t.i({ className: "ri-download-line", ariaHidden: true }), ), t.button( { type: "button", ariaLabel: app.attrs.tooltip("Restore"), className: () => `btn sm circle secondary transparent`, disabled: () => data.isDeleting[backup.key] || data.isDownloading[backup.key], onclick: () => openBackupRestoreModal(backup.key), }, t.i({ className: "ri-restart-line", ariaHidden: true }), ), t.button( { type: "button", ariaLabel: app.attrs.tooltip("Delete"), className: () => `btn sm circle secondary transparent ${ data.isDeleting[backup.key] ? "loading" : "" }`, disabled: () => data.isDeleting[backup.key] || data.isDownloading[backup.key], onclick: () => confirmBackupDelete(backup.key), }, t.i({ className: "ri-delete-bin-7-line", ariaHidden: true }), ), ), ); }); }, ), t.div( { className: "list-item" }, t.button( { type: "button", className: () => `btn secondary block ${data.isLoading ? "loading" : ""}`, disabled: () => !data.canBackup || data.isLoading, onclick: () => { openBackupCreateModal({ oncreated: () => loadBackups(), }); }, }, () => { if (data.canBackup) { return [ t.i({ className: "ri-play-circle-line", ariaHidden: true }), t.span({ className: "txt" }, "Initialize new backup"), ]; } return [ t.span({ className: "loader sm" }), t.span({ className: "txt" }, "Backup/restore operation is in process"), ]; }, ), ), ); }