fix: make dashboard login responsive
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -195,7 +195,7 @@
|
||||
<div class="dialog-icon">T</div>
|
||||
<p class="eyebrow">Защищённый доступ</p>
|
||||
<h2>Вход в TradeBot</h2>
|
||||
<p>Введите логин и пароль панели управления. Данные используются только для запросов из этой вкладки и не сохраняются в браузере.</p>
|
||||
<p>Введите логин и пароль панели управления TradeBot. Данные используются только для запросов из этой вкладки и не сохраняются в браузере.</p>
|
||||
<div class="auth-fields">
|
||||
<div>
|
||||
<label for="usernameInput">Логин</label>
|
||||
@@ -207,7 +207,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<p class="form-error" id="authError" role="alert"></p>
|
||||
<button class="button button-primary button-wide" type="submit">Войти</button>
|
||||
<button class="button button-primary button-wide" id="authSubmitButton" type="submit">Войти</button>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
@@ -221,6 +221,6 @@
|
||||
</dialog>
|
||||
|
||||
<div class="toast" id="toast" role="status" aria-live="polite"></div>
|
||||
<script src="/assets/dashboard.js?v=4" defer></script>
|
||||
<script src="/assets/dashboard.js?v=5" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user