fix: stabilize console scrolling and protect administrator login

This commit is contained in:
2026-09-30 00:09:01 +08:00
parent 9a15406f76
commit f56f233704
32 changed files with 1308 additions and 86 deletions

View File

@@ -69,7 +69,31 @@
// 初次访问时 URL 片段不会发给服务端,浏览器会把它保留在跳转后的登录地址上。
if (!returnTo.includes('#') && /^#\/(overview|tasks|schedules|workflows|subscriptions|retention|archives|applications)$/.test(location.hash)) returnTo += location.hash;
fetch('/console/auth/session', { headers: { Accept: 'application/json' }, credentials: 'same-origin', cache: 'no-store' }).then(response => response.json()).then(body => { if (body.authenticated) location.replace(returnTo); }).catch(() => {});
form.addEventListener('submit', async event => { event.preventDefault(); error.textContent = ''; submit.disabled = true; submit.textContent = '正在登录…'; try { const response = await fetch('/console/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, credentials: 'same-origin', body: JSON.stringify({ username: username.value.trim(), password: password.value }) }); const body = await response.json().catch(() => ({})); if (!response.ok) throw new Error(body?.error?.message || '登录失败,请检查用户名和密码'); location.replace(returnTo); } catch (loginError) { error.textContent = loginError.message || '登录失败,请稍后重试'; password.select(); } finally { submit.disabled = false; submit.textContent = '登录 Orbit →'; } });
let retryUntil = 0, retryTimer;
function showRetry() {
const remaining = Math.max(0, Math.ceil((retryUntil - Date.now()) / 1000));
submit.disabled = remaining > 0;
submit.textContent = remaining ? `请等待 ${Math.floor(remaining / 60)}分 ${remaining % 60}秒` : '登录 Orbit →';
if (!remaining) clearInterval(retryTimer);
}
form.addEventListener('submit', async event => {
event.preventDefault(); if (Date.now() < retryUntil) return;
error.textContent = ''; submit.disabled = true; submit.textContent = '正在登录…';
try {
const response = await fetch('/console/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, credentials: 'same-origin', body: JSON.stringify({ username: username.value.trim(), password: password.value }) });
const body = await response.json().catch(() => ({}));
if (!response.ok) {
const seconds = Number(body?.error?.retry_after_seconds);
if (response.status === 429 && Number.isFinite(seconds) && seconds > 0) {
retryUntil = Date.now() + seconds * 1000;
clearInterval(retryTimer); retryTimer = setInterval(showRetry, 1000);
}
throw new Error(body?.error?.message || '登录失败,请稍后重试');
}
location.replace(returnTo);
} catch (loginError) { error.textContent = loginError.message || '登录失败,请稍后重试'; password.select(); }
finally { showRetry(); }
});
})();
</script>
</body>