v2.0.4 — re-auth on 401, robust bucket-switch test
- auth: when a background poll or save returns 401 (session expired while the UI is still open), the api() helpers now prompt for the password instead of letting the save fail silently. Concurrent 401s collapse onto a single dialog via a shared reauthPromise, and each caller retries its original request once after login. /api/auth/* is excluded from the retry path. Extracts promptLogin() in app.js and exposes it as window._reauth so both the core util.js api() and the plugin-SDK hyprcrd.js api() can share the flow. - tests: the "open-switches-bucket" detail test assumed buckets[0] was a plain top-level bucket, but the bar renders super-bucket parents with data-bucket-super and hides child buckets entirely, so the .bucket-tab.active[data-bucket] selector found nothing on setups with nested layouts. Test now picks two plain top-level buckets or skips. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
48b5907f4c
commit
f986a41ccc
6 files changed
+76
-28
No files matched your search
+27
-9
@@ -180,6 +180,32 @@ document.getElementById('logout-btn')?.addEventListener('click', async () => {
|
||||
location.reload();
|
||||
});
|
||||
|
||||
// Re-usable login prompt loop. Shared between the boot-time auth check
|
||||
// and the 401-driven re-auth path (session expiry while the UI is open).
|
||||
// Concurrent callers collapse onto a single dialog via reauthPromise so a
|
||||
// wave of background polls doesn't stack up N prompts.
|
||||
let reauthPromise = null;
|
||||
async function promptLogin(message = '') {
|
||||
if (reauthPromise) return reauthPromise;
|
||||
reauthPromise = (async () => {
|
||||
while (true) {
|
||||
const pw = await showDialog({ title: 'login', message, input: 'password', placeholder: '> password...', okLabel: 'Login', cancelLabel: '' });
|
||||
if (!pw) continue;
|
||||
const r = await fetch('api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: pw }) });
|
||||
const d = await r.json();
|
||||
if (d.ok) {
|
||||
if (d.csrf) { setCsrf(d.csrf); window.hyprcrd?.util?.setCsrf?.(d.csrf); }
|
||||
return;
|
||||
}
|
||||
// wrong password — loop and re-prompt
|
||||
}
|
||||
})();
|
||||
try { await reauthPromise; } finally { reauthPromise = null; }
|
||||
}
|
||||
// Expose for the api() helpers so a 401 from any fetch triggers the same
|
||||
// dialog + CSRF refresh flow.
|
||||
window._reauth = () => promptLogin('Session expired — sign in to continue.');
|
||||
|
||||
async function checkAuth() {
|
||||
const res = await fetch('api/auth/status');
|
||||
const data = await res.json();
|
||||
@@ -203,15 +229,7 @@ async function checkAuth() {
|
||||
if (d.error) { location.reload(); return; }
|
||||
if (d.csrf) { setCsrf(d.csrf); window.hyprcrd?.util?.setCsrf?.(d.csrf); }
|
||||
} else if (!data.logged_in) {
|
||||
let ok = false;
|
||||
while (!ok) {
|
||||
const pw = await showDialog({ title: 'login', message: '', input: 'password', placeholder: '> password...', okLabel: 'Login', cancelLabel: '' });
|
||||
if (!pw) continue;
|
||||
const r = await fetch('api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: pw }) });
|
||||
const d = await r.json();
|
||||
if (d.ok) { ok = true; if (d.csrf) { setCsrf(d.csrf); window.hyprcrd?.util?.setCsrf?.(d.csrf); } }
|
||||
else { /* failed login: caller will retry */ }
|
||||
}
|
||||
await promptLogin();
|
||||
}
|
||||
|
||||
loadBuckets();
|
||||
|
||||
Reference in new issue
Block a user