Ask before running a script, which the page header has always said it did

Run was the only mutating control here that did not confirm, and it is the one that executes
shell as root — the library holds cleaners that delete files, and six Run buttons sit a row apart
in a dense tree.
This commit is contained in:
Gmer4Lfe
2026-08-07 10:43:43 -04:00
parent 86a0ba55fe
commit 228aae31c9
+34 -15
View File
@@ -26,6 +26,10 @@
// //
// OPERATIONAL SAFEGUARDS // OPERATIONAL SAFEGUARDS
// Running a script from the UI is confirmed first. Several of these delete files. // Running a script from the UI is confirmed first. Several of these delete files.
// vvConfirmRun() guards all three entry points — the six Run buttons, the direct
// by-id run, and Git Pull. This line described intent rather than behaviour until
// 2026-08-07: Run was the only mutating control on the page that did not ask, while
// being the one that executes shell as root.
// //
// Dry-run is offered alongside run for anything destructive, and its output is shown in // Dry-run is offered alongside run for anything destructive, and its output is shown in
// full rather than summarised — reading the list is the point. // full rather than summarised — reading the list is the point.
@@ -1508,12 +1512,27 @@ function vvSelectLog(btn) {
vvOpenRight(job.dataset.id); vvOpenRight(job.dataset.id);
} }
// Run a script by ID directly — no DOM card needed. // Every other mutating control on this page already confirms — save, delete, move, conf save,
function vvRunById(id) { // lock clear. Run did not, and it is the one that executes shell as root: the library holds
// cleaners that delete files and the git pull itself, and the six Run buttons sit one row apart
// in a dense tree where the row under the cursor is easy to misjudge. The page header has
// promised this confirmation since it was written; the code never did it.
//
// Dry Run is named in the prompt on purpose. For anything destructive it is the actual answer to
// "are you sure", and it is the button immediately beside the one that raised the question.
function vvConfirmRun(id) {
return confirm('Run ' + id + ' now?\n\n'
+ 'It starts immediately and as root, with the same effect as a scheduled run.\n'
+ 'Use Dry Run first if you want to see what it would change.');
}
// The confirm lives in the callers rather than here, so a caller that has already asked — or has
// button state to manage on cancel, as the pull does — is not forced to ask twice.
function vvRunStart(id, data) {
vvOpenRight(id); vvOpenRight(id);
vvSetDot(id); vvSetDot(id);
vvRunningSet.add(id); vvRunningSet.add(id);
vvPost('/plugins/varaverk/api/run.php', {id, manual: '1'}) return vvPost('/plugins/varaverk/api/run.php', data)
.then(d => { .then(d => {
if (!d.ok) { if (!d.ok) {
document.getElementById('vv-log-pre').textContent = '✗ ' + (d.error ?? 'Failed to start'); document.getElementById('vv-log-pre').textContent = '✗ ' + (d.error ?? 'Failed to start');
@@ -1523,11 +1542,20 @@ function vvRunById(id) {
}); });
} }
// Run a script by ID directly — no DOM card needed.
function vvRunById(id) {
if (!vvConfirmRun(id)) return;
vvRunStart(id, {id, manual: '1'});
}
function vvGitPull(btn) { function vvGitPull(btn) {
const id = 'git_pull_execute.sh'; const id = 'git_pull_execute.sh';
// Asked before the button is disabled, so cancelling does not leave it stuck reading "Pulling…"
// for the next four seconds while nothing is pulling.
if (!vvConfirmRun(id)) return;
btn.disabled = true; btn.disabled = true;
btn.textContent = '⟳ Pulling…'; btn.textContent = '⟳ Pulling…';
vvRunById(id); vvRunStart(id, {id, manual: '1'});
// Re-enable once the log poll confirms it's running or done // Re-enable once the log poll confirms it's running or done
setTimeout(() => { btn.disabled = false; btn.textContent = '↻ Git Pull'; }, 4000); setTimeout(() => { btn.disabled = false; btn.textContent = '↻ Git Pull'; }, 4000);
} }
@@ -1535,22 +1563,13 @@ function vvGitPull(btn) {
function vvRunJob(btn) { function vvRunJob(btn) {
const job = btn.closest('[data-id]'); const job = btn.closest('[data-id]');
const id = job.dataset.id; const id = job.dataset.id;
if (!vvConfirmRun(id)) return;
const location = job.querySelector('.vv-rsync-location')?.value.trim() || ''; const location = job.querySelector('.vv-rsync-location')?.value.trim() || '';
const extra_args = job.querySelector('.vv-script-args')?.value.trim() || ''; const extra_args = job.querySelector('.vv-script-args')?.value.trim() || '';
vvOpenRight(id);
vvSetDot(id);
vvRunningSet.add(id);
const data = {id}; const data = {id};
if (location) data.location = location; if (location) data.location = location;
if (extra_args) data.extra_args = extra_args; if (extra_args) data.extra_args = extra_args;
vvPost('/plugins/varaverk/api/run.php', data) vvRunStart(id, data);
.then(d => {
if (!d.ok) {
document.getElementById('vv-log-pre').textContent = '✗ ' + (d.error ?? 'Failed to start');
vvClearDot(id);
vvRunningSet.delete(id);
}
});
} }
function vvDryRun(btn) { function vvDryRun(btn) {