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 => ``).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');