fix: expose dashboard login shell
This commit is contained in:
@@ -338,6 +338,7 @@ tbody tr:hover td { background: rgba(255,255,255,.012); }
|
||||
.dialog p:not(.eyebrow):not(.form-error) { margin: 11px 0 22px; color: var(--muted); font-size: 11px; line-height: 1.6; }
|
||||
.dialog label { display: block; margin-bottom: 8px; color: var(--muted); font-size: 10px; }
|
||||
.dialog input { width: 100%; height: 40px; padding: 0 11px; border: 1px solid var(--line); border-radius: 7px; outline: 0; background: #090c10; color: var(--text); }
|
||||
.auth-fields { display: grid; gap: 14px; }
|
||||
.dialog .button-wide { margin-top: 14px; }
|
||||
.form-error { min-height: 16px; margin: 8px 0 0; color: var(--red); font-size: 9px; }
|
||||
.dialog-actions { display: flex; justify-content: flex-end; gap: 9px; }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const state = {
|
||||
snapshot: null,
|
||||
token: "",
|
||||
authorization: "",
|
||||
loading: false,
|
||||
timer: null,
|
||||
marketFilter: "",
|
||||
@@ -32,7 +32,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
bindNavigation();
|
||||
bindControls();
|
||||
selectPage(location.hash.slice(1) || "overview", false);
|
||||
loadSnapshot();
|
||||
showAuthDialog();
|
||||
});
|
||||
|
||||
function bindNavigation() {
|
||||
@@ -76,17 +76,26 @@ function bindControls() {
|
||||
});
|
||||
$("#authForm").addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
state.token = $("#tokenInput").value.trim();
|
||||
const username = $("#usernameInput").value.trim();
|
||||
const password = $("#passwordInput").value;
|
||||
setText("authError", "");
|
||||
if (!state.token) return;
|
||||
if (!username || !password) return;
|
||||
state.authorization = basicAuthorization(username, password);
|
||||
await loadSnapshot(true, true);
|
||||
});
|
||||
}
|
||||
|
||||
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.token) headers["X-TradeBot-Token"] = state.token;
|
||||
if (state.authorization) headers.Authorization = state.authorization;
|
||||
const response = await fetch(path, {
|
||||
...options,
|
||||
headers,
|
||||
@@ -115,11 +124,13 @@ async function loadSnapshot(manual = false, fromAuth = false) {
|
||||
render(snapshot);
|
||||
setOffline(false);
|
||||
if ($("#authDialog").open) $("#authDialog").close();
|
||||
$("#passwordInput").value = "";
|
||||
setText("authError", "");
|
||||
scheduleRefresh(10000);
|
||||
} catch (error) {
|
||||
if (error instanceof AuthRequiredError) {
|
||||
if (fromAuth) setText("authError", "Токен не принят сервером.");
|
||||
if (fromAuth) setText("authError", "Неверный логин или пароль.");
|
||||
state.authorization = "";
|
||||
showAuthDialog();
|
||||
} else {
|
||||
setOffline(true, error.message);
|
||||
@@ -140,7 +151,7 @@ function showAuthDialog() {
|
||||
const dialog = $("#authDialog");
|
||||
if (!dialog.open) dialog.showModal();
|
||||
setText("syncLabel", "Нужна авторизация");
|
||||
setTimeout(() => $("#tokenInput").focus(), 50);
|
||||
setTimeout(() => $("#usernameInput").focus(), 50);
|
||||
}
|
||||
|
||||
function setOffline(offline, message = "") {
|
||||
|
||||
@@ -194,12 +194,20 @@
|
||||
<form id="authForm">
|
||||
<div class="dialog-icon">T</div>
|
||||
<p class="eyebrow">Защищённый доступ</p>
|
||||
<h2>Требуется API-токен</h2>
|
||||
<p>Прокси-авторизация не обнаружена. Токен останется только в памяти этой вкладки и не будет сохранён.</p>
|
||||
<label for="tokenInput">Токен TradeBot</label>
|
||||
<input id="tokenInput" type="password" autocomplete="current-password" required>
|
||||
<h2>Вход в TradeBot</h2>
|
||||
<p>Введите логин и пароль панели управления. Данные используются только для запросов из этой вкладки и не сохраняются в браузере.</p>
|
||||
<div class="auth-fields">
|
||||
<div>
|
||||
<label for="usernameInput">Логин</label>
|
||||
<input id="usernameInput" type="text" autocomplete="username" required>
|
||||
</div>
|
||||
<div>
|
||||
<label for="passwordInput">Пароль</label>
|
||||
<input id="passwordInput" type="password" autocomplete="current-password" required>
|
||||
</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" type="submit">Войти</button>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
@@ -213,6 +221,6 @@
|
||||
</dialog>
|
||||
|
||||
<div class="toast" id="toast" role="status" aria-live="polite"></div>
|
||||
<script src="/assets/dashboard.js?v=3" defer></script>
|
||||
<script src="/assets/dashboard.js?v=4" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user