fix: make dashboard login responsive

This commit is contained in:
Курнат Андрей
2026-07-19 22:56:19 +03:00
parent 393454c9e0
commit 3ce92b6428
3 changed files with 50 additions and 24 deletions
+43 -19
View File
@@ -2,7 +2,7 @@
const state = {
snapshot: null,
authorization: "",
token: "",
loading: false,
timer: null,
marketFilter: "",
@@ -78,30 +78,49 @@ function bindControls() {
event.preventDefault();
const username = $("#usernameInput").value.trim();
const password = $("#passwordInput").value;
const submitButton = $("#authSubmitButton");
setText("authError", "");
if (!username || !password) return;
state.authorization = basicAuthorization(username, password);
await loadSnapshot(true, true);
if (username !== "sevenhill") {
setText("authError", "Неверный логин или пароль.");
return;
}
state.token = password.trim();
submitButton.disabled = true;
submitButton.setAttribute("aria-busy", "true");
setText("authSubmitButton", "Входим…");
setText("authError", "Проверяем доступ…");
try {
await loadSnapshot(true, true);
} finally {
submitButton.disabled = false;
submitButton.removeAttribute("aria-busy");
setText("authSubmitButton", "Войти");
}
});
}
function basicAuthorization(username, password) {
const bytes = new TextEncoder().encode(`${username}:${password}`);
let binary = "";
bytes.forEach((byte) => { binary += String.fromCharCode(byte); });
return `Basic ${btoa(binary)}`;
}
async function api(path, options = {}) {
const headers = { Accept: "application/json", ...(options.headers || {}) };
if (options.body) headers["Content-Type"] = "application/json";
if (state.authorization) headers.Authorization = state.authorization;
const response = await fetch(path, {
...options,
headers,
credentials: "same-origin",
cache: "no-store",
});
if (state.token) headers["X-TradeBot-Token"] = state.token;
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 30000);
let response;
try {
response = await fetch(path, {
...options,
headers,
signal: controller.signal,
credentials: "omit",
cache: "no-store",
});
} catch (error) {
if (error?.name === "AbortError") throw new Error("Сервер не ответил за 30 секунд.");
throw error;
} finally {
clearTimeout(timeout);
}
if (response.status === 401) throw new AuthRequiredError("Требуется авторизация");
let payload = null;
try { payload = await response.json(); } catch (_) { payload = null; }
@@ -113,7 +132,7 @@ async function api(path, options = {}) {
}
async function loadSnapshot(manual = false, fromAuth = false) {
if (state.loading) return;
if (state.loading) return false;
state.loading = true;
clearTimeout(state.timer);
$("#refreshButton").classList.add("is-spinning");
@@ -127,15 +146,20 @@ async function loadSnapshot(manual = false, fromAuth = false) {
$("#passwordInput").value = "";
setText("authError", "");
scheduleRefresh(10000);
return true;
} catch (error) {
if (error instanceof AuthRequiredError) {
if (fromAuth) setText("authError", "Неверный логин или пароль.");
state.authorization = "";
state.token = "";
showAuthDialog();
} else if (fromAuth) {
setText("authError", `Ошибка подключения: ${error.message}`);
showAuthDialog();
} else {
setOffline(true, error.message);
scheduleRefresh(12000);
}
return false;
} finally {
state.loading = false;
$("#refreshButton").classList.remove("is-spinning");