Files
Grav-Admin-Plugin/themes/grav/app/tools/restore.js
2025-10-18 18:26:38 -06:00

175 lines
5.7 KiB
JavaScript

import $ from 'jquery';
import { config, translations } from 'grav-config';
import request from '../utils/request';
import toastr from '../utils/toastr';
const paramSep = config.param_sep;
const task = `task${paramSep}`;
const nonce = `admin-nonce${paramSep}${config.admin_nonce}`;
const base = `${config.base_url_relative}/update.json`;
const urls = {
restore: `${base}/${task}safeUpgradeRestore/${nonce}`,
status: `${base}/${task}safeUpgradeStatus/${nonce}`,
};
class RestoreManager {
constructor() {
this.job = null;
this.pollTimer = null;
this.pollFailures = 0;
$(document).on('click', '[data-restore-snapshot]', (event) => {
event.preventDefault();
const button = $(event.currentTarget);
if (this.job) {
return;
}
this.startRestore(button);
});
}
startRestore(button) {
const snapshot = button.data('restore-snapshot');
if (!snapshot) {
return;
}
button.prop('disabled', true).addClass('is-loading');
const body = { snapshot };
request(urls.restore, { method: 'post', body }, (response) => {
button.prop('disabled', false).removeClass('is-loading');
if (!response) {
toastr.error(translations.PLUGIN_ADMIN?.RESTORE_GRAV_FAILED || 'Snapshot restore failed.');
return;
}
if (response.status === 'error') {
toastr.error(response.message || translations.PLUGIN_ADMIN?.RESTORE_GRAV_FAILED || 'Snapshot restore failed.');
return;
}
const data = response.data || {};
const jobId = data.job_id || (data.job && data.job.id);
if (!jobId) {
const message = response.message || translations.PLUGIN_ADMIN?.RESTORE_GRAV_FAILED || 'Snapshot restore failed.';
toastr.error(message);
return;
}
this.job = {
id: jobId,
snapshot,
};
this.pollFailures = 0;
const runningMessage = translations.PLUGIN_ADMIN?.RESTORE_GRAV_RUNNING
? translations.PLUGIN_ADMIN.RESTORE_GRAV_RUNNING.replace('%s', snapshot)
: `Restoring snapshot ${snapshot}...`;
toastr.info(runningMessage);
this.schedulePoll();
});
}
schedulePoll(delay = 1200) {
this.clearPoll();
this.pollTimer = setTimeout(() => this.pollStatus(), delay);
}
clearPoll() {
if (this.pollTimer) {
clearTimeout(this.pollTimer);
this.pollTimer = null;
}
}
pollStatus() {
if (!this.job) {
return;
}
const jobId = this.job.id;
let handled = false;
request(`${urls.status}?job=${encodeURIComponent(jobId)}`, { silentErrors: true }, (response) => {
handled = true;
this.pollFailures = 0;
if (!response || response.status !== 'success') {
this.schedulePoll();
return;
}
const data = response.data || {};
const job = data.job || {};
const progress = data.progress || {};
const stage = progress.stage || null;
const status = job.status || progress.status || null;
if (stage === 'error' || status === 'error') {
const message = job.error || progress.message || translations.PLUGIN_ADMIN?.RESTORE_GRAV_FAILED || 'Snapshot restore failed.';
toastr.error(message);
this.job = null;
this.clearPoll();
return;
}
if (stage === 'complete' || status === 'success') {
const snapshot = progress.snapshot || this.job.snapshot;
const version = (job.result && job.result.version) || progress.version || '';
let successMessage;
if (translations.PLUGIN_ADMIN?.RESTORE_GRAV_SUCCESS_MESSAGE && version) {
successMessage = translations.PLUGIN_ADMIN.RESTORE_GRAV_SUCCESS_MESSAGE.replace('%1$s', snapshot).replace('%2$s', version);
} else if (translations.PLUGIN_ADMIN?.RESTORE_GRAV_SUCCESS_SIMPLE) {
successMessage = translations.PLUGIN_ADMIN.RESTORE_GRAV_SUCCESS_SIMPLE.replace('%s', snapshot);
} else {
successMessage = version ? `Snapshot ${snapshot} restored (Grav ${version}).` : `Snapshot ${snapshot} restored.`;
}
toastr.success(successMessage);
this.job = null;
this.clearPoll();
setTimeout(() => window.location.reload(), 1500);
return;
}
this.schedulePoll();
}).then(() => {
if (!handled) {
this.handleSilentFailure();
}
});
}
handleSilentFailure() {
if (!this.job) {
return;
}
this.pollFailures += 1;
const snapshot = this.job.snapshot || '';
if (this.pollFailures >= 3) {
const message = snapshot
? `Snapshot ${snapshot} restore is completing. Reloading...`
: 'Snapshot restore is completing. Reloading...';
toastr.info(message);
this.job = null;
this.clearPoll();
setTimeout(() => window.location.reload(), 1500);
return;
}
const delay = Math.min(5000, 1200 * this.pollFailures);
this.schedulePoll(delay);
}
}
// Initialize restore manager when tools view loads.
$(document).ready(() => {
new RestoreManager();
});