diff --git a/Plugin/unraid/Varaverk.page b/Plugin/unraid/Varaverk.page
index 6400179..384ed9e 100644
--- a/Plugin/unraid/Varaverk.page
+++ b/Plugin/unraid/Varaverk.page
@@ -147,7 +147,19 @@ function vvConfirm(text, opts) {
}
// Telling the operator something, with no question attached. Returns a promise so a caller can
-// sequence on it, but nothing has to await it.
+// sequence on it, but nothing has to await it — which is what let the fifty-odd alert() sites
+// become this by rename alone, with no function above them turning async.
+//
+// The icon is read off the message when the caller does not say. Almost every one of these
+// reports a failure — "Save failed: …", "Error: …" — and asking fifty call sites to each classify
+// themselves would mean fifty chances to disagree about what counts as an error. The caller can
+// still pass type explicitly and that always wins.
+function vvAlertType(text) {
+ const s = String(text ?? '');
+ if (/\b(fail|failed|error|denied|invalid|refused|cannot|could not|unable)\b/i.test(s)) return 'error';
+ if (/\b(warn|warning|already|must be)\b/i.test(s)) return 'warning';
+ return 'info';
+}
function vvAlert(text, opts) {
const o = opts || {};
return new Promise(function (resolve) {
@@ -155,7 +167,7 @@ function vvAlert(text, opts) {
swal({
title: o.title || '',
text: String(text ?? ''),
- type: o.type || 'info',
+ type: o.type || vvAlertType(text),
confirmButtonText: o.confirmText || 'OK',
}, function (ok) { resolve(true); });
});
diff --git a/Plugin/unraid/pages/auth.php b/Plugin/unraid/pages/auth.php
index 373a514..3d12ffc 100644
--- a/Plugin/unraid/pages/auth.php
+++ b/Plugin/unraid/pages/auth.php
@@ -649,7 +649,7 @@ function _addToGroupModal(uid) {
const user = _users.find(u => u.id === uid);
const userGids= new Set((user?.groups||[]).map(g => g.id));
const available = _groups.filter(g => !userGids.has(g.id));
- if (!available.length) { alert('User is already in all groups.'); return; }
+ if (!available.length) { vvAlert('User is already in all groups.'); return; }
const opts = available.map(g => `${_esc(g.displayName)} `).join('');
_modal(`
Add to Group
@@ -936,7 +936,7 @@ document.getElementById('vv-au-panel-acl').addEventListener('click', async e =>
btn.disabled = true; btn.textContent = 'Saving…';
_post({ action:'authelia_save', rules: JSON.stringify(_rules), default_policy: dp }, r => {
btn.disabled = false; btn.textContent = 'Save & Restart Authelia';
- if (!r.ok) { alert('Save failed: ' + (r.error||'unknown error')); return; }
+ if (!r.ok) { vvAlert('Save failed: ' + (r.error||'unknown error')); return; }
// brief visual confirmation
btn.textContent = 'Saved ✓';
setTimeout(() => { btn.textContent = 'Save & Restart Authelia'; }, 2000);
diff --git a/Plugin/unraid/pages/docker.php b/Plugin/unraid/pages/docker.php
index ec700ce..bafad26 100644
--- a/Plugin/unraid/pages/docker.php
+++ b/Plugin/unraid/pages/docker.php
@@ -369,7 +369,7 @@ function _doRestart(name) {
function _doStartStop(name, start) {
_actApi({action: start ? 'start' : 'stop', name}, data => {
if (data.ok) setTimeout(_reload, 800);
- else alert((start ? 'Start' : 'Stop') + ' failed: ' + (data.output || data.error || '?'));
+ else vvAlert((start ? 'Start' : 'Stop') + ' failed: ' + (data.output || data.error || '?'));
});
}
@@ -378,7 +378,7 @@ function _doPullRebuild(name) {
if (data.ok && data.job_id) {
_startJob(name, data.job_id);
} else {
- alert('Update failed: ' + (data.error || '?'));
+ vvAlert('Update failed: ' + (data.error || '?'));
}
});
}
@@ -478,7 +478,7 @@ function _bindEvents() {
el.disabled = true; el.style.opacity = '0.5';
_api({action:'rename_folder',folder_id:fid,name}, r => {
el.disabled = false; el.style.opacity = '';
- if (r.ok) _reload(); else { alert('Rename failed: '+(r.error||'?')); el.value = f?.name ?? name; }
+ if (r.ok) _reload(); else { vvAlert('Rename failed: '+(r.error||'?')); el.value = f?.name ?? name; }
});
});
});
@@ -490,14 +490,14 @@ function _bindEvents() {
const fid = el.dataset.deleteFolder;
const f = (_data.folders||[]).find(x=>x.id===fid);
if (!f || !await vvConfirm(`Delete "${f.name}"? Containers will be ungrouped.`)) return;
- _api({action:'delete_folder',folder_id:fid}, r => { if(r.ok) _reload(); else alert('Delete failed: '+(r.error||'?')); });
+ _api({action:'delete_folder',folder_id:fid}, r => { if(r.ok) _reload(); else vvAlert('Delete failed: '+(r.error||'?')); });
});
});
// New folder
document.getElementById('vv-dk-add-folder')?.addEventListener('click', async () => {
const name = await vvPrompt('New folder name:');
- if (name?.trim()) _api({action:'create_folder',name:name.trim()}, r => { if(r.ok) _reload(); else alert(r.error); });
+ if (name?.trim()) _api({action:'create_folder',name:name.trim()}, r => { if(r.ok) _reload(); else vvAlert(r.error); });
});
}
@@ -527,11 +527,11 @@ function _showPopover(x, y) {
const name = await vvPrompt('New folder name:');
if (!name?.trim()) return;
_api({action:'create_folder',name:name.trim()}, r => {
- if (!r.ok) { alert(r.error); return; }
+ if (!r.ok) { vvAlert(r.error); return; }
_api({action:'move_container',container:_popTarget.container,folder_id:r.id}, r2 => { if(r2.ok) _reload(); });
});
} else {
- _api({action:'move_container',container:_popTarget.container,folder_id:fid}, r => { if(r.ok) _reload(); else alert(r.error); });
+ _api({action:'move_container',container:_popTarget.container,folder_id:fid}, r => { if(r.ok) _reload(); else vvAlert(r.error); });
}
});
});
@@ -555,12 +555,12 @@ document.getElementById('vv-dk-edit-toggle').addEventListener('click', function(
});
function _syncC2J() {
- _api({action:'sync_conf_to_json'}, r => { if(r.ok) _reload(); else alert(r.error); });
+ _api({action:'sync_conf_to_json'}, r => { if(r.ok) _reload(); else vvAlert(r.error); });
}
document.getElementById('vv-dk-sync-c2j').addEventListener('click', _syncC2J);
document.getElementById('vv-dk-sync-j2c').addEventListener('click', async () => {
if (!await vvConfirm('Overwrite conf map with current JSON state?')) return;
- _api({action:'sync_json_to_conf'}, r => { if(r.ok) _reload(); else alert(r.error); });
+ _api({action:'sync_json_to_conf'}, r => { if(r.ok) _reload(); else vvAlert(r.error); });
});
// ── Load ──────────────────────────────────────────────────────────────────────
diff --git a/Plugin/unraid/pages/monitor.php b/Plugin/unraid/pages/monitor.php
index 0d68002..b83d661 100644
--- a/Plugin/unraid/pages/monitor.php
+++ b/Plugin/unraid/pages/monitor.php
@@ -2636,14 +2636,14 @@ async function vvDockerAction(action, name, webui) {
// The endpoint reports refusals as ok:false with a reason — container not found, a non-zero
// docker exit. Discarding that made a failed stop look exactly like a successful one, since
// the card it would have changed is redrawn from a payload either way.
- if (!d || !d.ok) alert('Container ' + action + ' failed: ' + ((d && (d.error || d.output)) || 'unknown error'));
+ if (!d || !d.ok) vvAlert('Container ' + action + ' failed: ' + ((d && (d.error || d.output)) || 'unknown error'));
vvDfActive = null;
// The endpoint drops the monitor cache on success, and this poll asks for a live collection
// besides — either alone is enough, but between them the card cannot redraw itself from a
// payload assembled before the action happened.
setTimeout(() => vvPollMonitor(true), 1500);
})
- .catch(() => alert('Container ' + action + ' failed: request error'));
+ .catch(() => vvAlert('Container ' + action + ' failed: request error'));
}
function vvRenderDockerFolders(data) {
diff --git a/Plugin/unraid/pages/partnership.php b/Plugin/unraid/pages/partnership.php
index 6b401d4..cc63cd3 100644
--- a/Plugin/unraid/pages/partnership.php
+++ b/Plugin/unraid/pages/partnership.php
@@ -215,12 +215,12 @@ function vvApiKey(btn) {
if (d.ok) {
if (_vvPtReload) _vvPtReload();
} else {
- alert('Failed: ' + (d.error ?? 'Unknown error'));
+ vvAlert('Failed: ' + (d.error ?? 'Unknown error'));
btn.disabled = false; btn.textContent = origText;
}
})
.catch(e => {
- alert('Error: ' + e);
+ vvAlert('Error: ' + e);
btn.disabled = false; btn.textContent = origText;
});
}
@@ -230,8 +230,8 @@ async function vvPtPhase2(btn, hostId) {
btn.disabled = true;
btn.textContent = '⟳ Starting…';
_vvPtRun('Partnership/partnership_onboard.sh', '--phase2-only')
- .then(d => { if (!d.ok) alert('Failed: ' + (d.error ?? 'Unknown error')); })
- .catch(e => alert('Error: ' + e))
+ .then(d => { if (!d.ok) vvAlert('Failed: ' + (d.error ?? 'Unknown error')); })
+ .catch(e => vvAlert('Error: ' + e))
.finally(() => setTimeout(() => { btn.disabled = false; btn.textContent = '▶ Run Phase 2 Manually'; }, 3000));
}
@@ -240,8 +240,8 @@ async function vvPtOnboard(btn) {
btn.disabled = true;
btn.textContent = '⟳ Starting…';
_vvPtRun('Partnership/partnership_onboard.sh')
- .then(d => { if (!d.ok) alert('Failed: ' + (d.error ?? 'Unknown error')); })
- .catch(e => alert('Error: ' + e))
+ .then(d => { if (!d.ok) vvAlert('Failed: ' + (d.error ?? 'Unknown error')); })
+ .catch(e => vvAlert('Error: ' + e))
.finally(() => setTimeout(() => { btn.disabled = false; btn.textContent = '▶ Onboard (Mirror)'; }, 4000));
}
@@ -250,8 +250,8 @@ async function vvPtPushConf(btn, hostId) {
btn.disabled = true;
btn.textContent = '⟳ Pushing…';
_vvPtRun('Partnership/partnership_onboard.sh', '--phase1-only --skip-ssh')
- .then(d => { if (!d.ok) alert('Failed: ' + (d.error ?? 'Unknown error')); })
- .catch(e => alert('Error: ' + e))
+ .then(d => { if (!d.ok) vvAlert('Failed: ' + (d.error ?? 'Unknown error')); })
+ .catch(e => vvAlert('Error: ' + e))
.finally(() => setTimeout(() => { btn.disabled = false; btn.textContent = '▶ Push Conf (key installed)'; }, 4000));
}
@@ -260,8 +260,8 @@ async function vvPtLocalSetup(btn) {
btn.disabled = true;
btn.textContent = '⟳ Running…';
_vvPtRun('Partnership/partnership_manager.sh', '--onboard --local-only')
- .then(d => { if (!d.ok) alert('Failed: ' + (d.error ?? 'Unknown error')); })
- .catch(e => alert('Error: ' + e))
+ .then(d => { if (!d.ok) vvAlert('Failed: ' + (d.error ?? 'Unknown error')); })
+ .catch(e => vvAlert('Error: ' + e))
.finally(() => setTimeout(() => { btn.disabled = false; btn.textContent = '▶ Complete HOST1 Setup'; }, 4000));
}
@@ -270,8 +270,8 @@ async function vvPtCancel(btn, hostId) {
btn.disabled = true;
btn.textContent = '⟳ Cancelling…';
_vvPtRun('Partnership/onboard_cancel.sh', '--direction=both')
- .then(d => { if (!d.ok) alert('Failed: ' + (d.error ?? 'Unknown error')); })
- .catch(e => alert('Error: ' + e))
+ .then(d => { if (!d.ok) vvAlert('Failed: ' + (d.error ?? 'Unknown error')); })
+ .catch(e => vvAlert('Error: ' + e))
.finally(() => setTimeout(() => { btn.disabled = false; btn.textContent = '✕ Cancel'; }, 4000));
}
@@ -279,8 +279,8 @@ async function vvPtDeleteH1(btn, hostId) {
if (!await vvConfirm(`Remove HOST1's key from ${hostId}?\n\n• Deletes local SSH key pair\n• Removes it from ${hostId}'s authorized_keys\n• Clears phase state`)) return;
btn.disabled = true; btn.textContent = '⟳ Removing…';
_vvPtRun('Partnership/onboard_cancel.sh', '--direction=h1')
- .then(d => { if (!d.ok) alert('Failed: ' + (d.error ?? 'Unknown error')); })
- .catch(e => alert('Error: ' + e))
+ .then(d => { if (!d.ok) vvAlert('Failed: ' + (d.error ?? 'Unknown error')); })
+ .catch(e => vvAlert('Error: ' + e))
.finally(() => setTimeout(() => { btn.disabled = false; btn.textContent = '✕ Remove HOST1 key'; }, 4000));
}
@@ -288,8 +288,8 @@ async function vvPtDeleteH2(btn, hostId) {
if (!await vvConfirm(`Remove ${hostId}'s key from HOST1?\n\n• Removes ${hostId}'s public key from HOST1's authorized_keys\n• ${hostId} will no longer be able to SSH into HOST1`)) return;
btn.disabled = true; btn.textContent = '⟳ Removing…';
_vvPtRun('Partnership/onboard_cancel.sh', '--direction=h2')
- .then(d => { if (!d.ok) alert('Failed: ' + (d.error ?? 'Unknown error')); })
- .catch(e => alert('Error: ' + e))
+ .then(d => { if (!d.ok) vvAlert('Failed: ' + (d.error ?? 'Unknown error')); })
+ .catch(e => vvAlert('Error: ' + e))
.finally(() => setTimeout(() => { btn.disabled = false; btn.textContent = `✕ Remove ${hostId} key`; }, 4000));
}
@@ -299,8 +299,8 @@ async function vvPtOffboard(btn) {
btn.disabled = true;
btn.textContent = '⟳ Starting…';
_vvPtRun('Partnership/partnership_offboard.sh')
- .then(d => { if (!d.ok) alert('Failed: ' + (d.error ?? 'Unknown error')); })
- .catch(e => alert('Error: ' + e))
+ .then(d => { if (!d.ok) vvAlert('Failed: ' + (d.error ?? 'Unknown error')); })
+ .catch(e => vvAlert('Error: ' + e))
.finally(() => setTimeout(() => { btn.disabled = false; btn.textContent = '▶ Offboard'; }, 4000));
}
@@ -311,8 +311,8 @@ async function vvPtTransfer(btn, token) {
btn.disabled = true;
btn.textContent = '⟳ Transferring…';
_vvPtRun('Partnership/partnership_transfer.sh', '--confirm=' + token)
- .then(d => { if (!d.ok) alert('Failed: ' + (d.error ?? 'Unknown error')); })
- .catch(e => alert('Error: ' + e))
+ .then(d => { if (!d.ok) vvAlert('Failed: ' + (d.error ?? 'Unknown error')); })
+ .catch(e => vvAlert('Error: ' + e))
.finally(() => setTimeout(() => { btn.disabled = false; btn.textContent = '⇄ Transfer Ownership'; }, 4000));
}
@@ -349,9 +349,9 @@ function vvPtToggleSync(el, varName, enabled) {
.then(r => r.json())
.then(d => {
if (d.ok) { if (_vvPtReload) _vvPtReload(); }
- else { alert('Failed: ' + (d.error ?? 'Unknown error')); el.style.pointerEvents = ''; el.style.opacity = ''; }
+ else { vvAlert('Failed: ' + (d.error ?? 'Unknown error')); el.style.pointerEvents = ''; el.style.opacity = ''; }
})
- .catch(e => { alert('Error: ' + e); el.style.pointerEvents = ''; el.style.opacity = ''; });
+ .catch(e => { vvAlert('Error: ' + e); el.style.pointerEvents = ''; el.style.opacity = ''; });
}
// ── Host Settings ─────────────────────────────────────────────────────────────
@@ -407,10 +407,10 @@ async function vvPtSaveHosts(btn) {
setTimeout(() => { btn.textContent = 'Save'; btn.style.display = 'none'; }, 2000);
} else {
btn.textContent = 'Save';
- alert('Save failed: ' + (d.error ?? 'Unknown error'));
+ vvAlert('Save failed: ' + (d.error ?? 'Unknown error'));
}
})
- .catch(e => { btn.disabled = false; btn.textContent = 'Save'; alert('Error: ' + e); });
+ .catch(e => { btn.disabled = false; btn.textContent = 'Save'; vvAlert('Error: ' + e); });
}
// ── Array settings cards ───────────────────────────────────────────────────────
@@ -507,10 +507,10 @@ async function vvPtSaveArrays(btn) {
setTimeout(() => _vvInitArrayCards(), 600);
} else {
btn.textContent = 'Save Changes';
- alert('Save failed: ' + (d.error ?? 'Unknown error'));
+ vvAlert('Save failed: ' + (d.error ?? 'Unknown error'));
}
})
- .catch(e => { btn.disabled = false; btn.textContent = 'Save Changes'; alert('Error: ' + e); });
+ .catch(e => { btn.disabled = false; btn.textContent = 'Save Changes'; vvAlert('Error: ' + e); });
}
// ── Settings panel ─────────────────────────────────────────────────────────────
@@ -620,10 +620,10 @@ async function vvPtSaveSettings(btn) {
setTimeout(() => { btn.textContent = 'Save Changes'; btn.style.display = 'none'; }, 2000);
} else {
btn.textContent = 'Save Changes';
- alert('Save failed: ' + (d.error ?? 'Unknown error'));
+ vvAlert('Save failed: ' + (d.error ?? 'Unknown error'));
}
})
- .catch(e => { btn.disabled = false; btn.textContent = 'Save Changes'; alert('Error: ' + e); });
+ .catch(e => { btn.disabled = false; btn.textContent = 'Save Changes'; vvAlert('Error: ' + e); });
}
// ── Private page logic ─────────────────────────────────────────────────────────
diff --git a/Plugin/unraid/pages/rsync.php b/Plugin/unraid/pages/rsync.php
index 46ea576..cc49bb0 100644
--- a/Plugin/unraid/pages/rsync.php
+++ b/Plugin/unraid/pages/rsync.php
@@ -1755,9 +1755,9 @@ function vvMsRun() {
const flags = Array.from(document.querySelectorAll('#vv-ms-card input[data-flag]:checked'))
.map(el => el.dataset.flag).join(' ');
- if (!local) { alert('Enter a local source path.'); return; }
- if (!slot) { alert('Select a remote server.'); return; }
- if (!rpath) { alert('Enter a remote destination path.'); return; }
+ if (!local) { vvAlert('Enter a local source path.'); return; }
+ if (!slot) { vvAlert('Select a remote server.'); return; }
+ if (!rpath) { vvAlert('Enter a remote destination path.'); return; }
const btn = document.getElementById('vv-ms-run-btn');
const stat = document.getElementById('vv-ms-run-status');
diff --git a/Plugin/unraid/pages/scheduler.php b/Plugin/unraid/pages/scheduler.php
index c4859d7..d886ab0 100644
--- a/Plugin/unraid/pages/scheduler.php
+++ b/Plugin/unraid/pages/scheduler.php
@@ -1917,7 +1917,7 @@ async function vvSaveScript() {
const name = document.getElementById('vv-editor-name').value.trim();
const content = document.getElementById('vv-editor-body').value;
if (!name || !/^[a-zA-Z0-9_\-]+$/.test(name)) {
- alert('Name must be letters, numbers, _ or - only (no spaces, no .sh)');
+ vvAlert('Name must be letters, numbers, _ or - only (no spaces, no .sh)');
return;
}
if (!await vvConfirm('Save changes to "' + name + '.sh"?')) return;
@@ -1926,7 +1926,7 @@ async function vvSaveScript() {
btn.textContent = 'Saving…';
vvPost('/plugins/varaverk/api/script.php', {name, content})
.then(d => {
- if (!d.ok) { alert('Save failed: ' + (d.error ?? 'Unknown error')); btn.disabled = false; btn.textContent = 'Save Script'; return; }
+ if (!d.ok) { vvAlert('Save failed: ' + (d.error ?? 'Unknown error')); btn.disabled = false; btn.textContent = 'Save Script'; return; }
localStorage.setItem('vv-last-job', d.id);
window.location.reload();
})
@@ -1942,7 +1942,7 @@ async function vvDeleteScript() {
btn.textContent = 'Deleting…';
vvPost('/plugins/varaverk/api/script.php', {action: 'delete', name})
.then(d => {
- if (!d.ok) { alert('Delete failed: ' + (d.error ?? 'Unknown error')); btn.disabled = false; btn.textContent = '\u{1F5D1} Delete'; return; }
+ if (!d.ok) { vvAlert('Delete failed: ' + (d.error ?? 'Unknown error')); btn.disabled = false; btn.textContent = '\u{1F5D1} Delete'; return; }
localStorage.removeItem('vv-last-job');
window.location.reload();
})
@@ -2119,16 +2119,16 @@ async function _vvImpDoImport() {
vvPost('/plugins/varaverk/api/import_script.php', {action: 'import', path: _vvImpSelected})
.then(d => {
if (!d.ok) {
- alert('Import failed: ' + (d.error || 'Unknown error'));
+ vvAlert('Import failed: ' + (d.error || 'Unknown error'));
btn.disabled = false;
btn.textContent = 'Import';
return;
}
- if (d.warning) alert(d.warning);
+ if (d.warning) vvAlert(d.warning);
window.location.reload();
})
.catch(e => {
- alert('Import failed: ' + e);
+ vvAlert('Import failed: ' + e);
btn.disabled = false;
btn.textContent = 'Import';
});
@@ -2270,7 +2270,7 @@ async function vvSaveConf() {
setTimeout(() => { btn.textContent = 'Save Config'; }, 2500);
} else {
btn.textContent = 'Save Config';
- alert('Save failed: ' + (d.error ?? 'Unknown error'));
+ vvAlert('Save failed: ' + (d.error ?? 'Unknown error'));
}
})
.catch(() => { btn.disabled = false; btn.textContent = 'Save Config'; });
@@ -3503,7 +3503,7 @@ async function vvSaveRawConf() {
btn.disabled = false;
if (!d.ok) {
btn.textContent = 'Save Conf';
- alert('Save failed: ' + (d.error ?? 'Unknown error'));
+ vvAlert('Save failed: ' + (d.error ?? 'Unknown error'));
return;
}
@@ -4772,7 +4772,7 @@ async function _vvFolderDrop(e) {
if (!r.ok) {
oldParent.appendChild(srcEl);
- alert('Failed to save folder assignment.');
+ vvAlert('Failed to save folder assignment.');
}
}
@@ -4921,12 +4921,12 @@ function vvNewFolder() {
async function _vvDoCreateFolder(name, wrap) {
if (!name) return;
const folders = vvGetCurrentFolders();
- if (folders[name] !== undefined) { alert(`Folder "${name}" already exists.`); return; }
+ if (folders[name] !== undefined) { vvAlert(`Folder "${name}" already exists.`); return; }
folders[name] = [];
const r = await vvPost('/plugins/varaverk/api/savefolders.php', {
folders: JSON.stringify(folders)
}).then(r => r.json()).catch(() => ({ ok: false }));
- if (!r.ok) { alert('Failed to create folder.'); return; }
+ if (!r.ok) { vvAlert('Failed to create folder.'); return; }
wrap.remove();
// Add folder group to DOM
const customChildren = document.getElementById('vv-custom-children');