diff --git a/public/app.js b/public/app.js index 0057200..4a62f6d 100644 --- a/public/app.js +++ b/public/app.js @@ -13,10 +13,12 @@ const runMsg = $("#run-msg"); const runningIndicator = $("#running-indicator"); const currentStep = $("#current-step"); const lastRunEl = $("#last-run"); +const xray2Section = $("#xray2-data-section"); const POLL_INTERVAL = 10000; let pollTimer = null; +let xray2Visible = false; function showScreen(screen) { loginScreen.classList.add("hidden"); @@ -27,25 +29,46 @@ function showScreen(screen) { function formatTime(iso) { if (!iso) return ""; const d = new Date(iso); - return d.toLocaleString(); + return d.toLocaleString("ru-RU", { + day: "2-digit", + month: "2-digit", + year: "numeric", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + }); } +const stepLabels = { + initializing: "Иніціализація...", + novavps_fetching: "Полученіе данныхъ NOVAVPS...", + xray1_parsing: "Чтеніе панели Xray 1...", + xray1_syncing: "Синхронизація Xray 1...", + xray2_parsing: "Чтеніе панели Xray 2...", + xray2_syncing: "Синхронизація Xray 2...", + done: "Завершено", + novavps_url_missing: "Ошибка: ссылка NOVAVPS не задана", + no_xray_panels: "Ошибка: нѣтъ настроенныхъ панелей Xray", + fatal: "Критическая ошибка", +}; + function updatePanelStatus(prefix, data) { const badge = $(`#${prefix}-status`); const detail = $(`#${prefix}-detail`); const time = $(`#${prefix}-time`); - badge.textContent = data.status; + const statusMap = { idle: "ожиданіе", success: "успѣшно", error: "ошибка" }; + badge.textContent = statusMap[data.status] || data.status; badge.className = "status-badge " + data.status; let detailText = ""; if (prefix === "novavps" && data.count != null) { - detailText = `${data.count} connections`; + detailText = `${data.count} подключеній`; } else if (data.outboundsCount != null) { - detailText = `${data.outboundsCount} outbounds`; - if (data.synced) detailText += " (synced)"; + detailText = `${data.outboundsCount} исходящихъ`; + if (data.synced) detailText += " · синхр."; } - if (data.error) detailText += (detailText ? " | " : "") + data.error; + if (data.error) detailText += (detailText ? " · " : "") + data.error; detail.textContent = detailText; time.textContent = formatTime(data.timestamp); } @@ -63,26 +86,17 @@ async function fetchStatus() { const isRunning = data.status === "running"; runBtn.disabled = isRunning; runningIndicator.classList.toggle("hidden", !isRunning); - - const stepLabels = { - initializing: "Initializing...", - novavps_fetching: "Fetching Novavps data...", - xray1_parsing: "Parsing Xray 1...", - xray1_syncing: "Syncing Xray 1...", - xray2_parsing: "Parsing Xray 2...", - xray2_syncing: "Syncing Xray 2...", - done: "Completed", - novavps_url_missing: "Error: NOVAVPS URL not set", - no_xray_panels: "Error: No Xray panels configured", - fatal: "Fatal error", - }; currentStep.textContent = stepLabels[data.currentStep] || data.currentStep; updatePanelStatus("novavps", data.novavps); updatePanelStatus("xray1", data.xray1); updatePanelStatus("xray2", data.xray2); - lastRunEl.textContent = data.lastRun ? formatTime(data.lastRun) : "Never"; + lastRunEl.textContent = data.lastRun ? formatTime(data.lastRun) : "Никогда"; + + if (data.xray2.status !== "idle" || data.xray2.outboundsCount > 0) { + xray2Section.classList.remove("hidden"); + } } catch (err) { console.error("Status fetch error:", err); } @@ -103,11 +117,90 @@ async function fetchConfig() { } } +async function fetchNovavpsData() { + try { + const res = await fetch("/api/data/novavps"); + if (res.status === 401) return; + const data = await res.json(); + renderNovavpsTable(data); + $("#novavps-data-count").textContent = data.length; + } catch {} +} + +async function fetchXrayData(panel, syncedTags) { + try { + const res = await fetch("/api/data/xray"); + if (res.status === 401) return; + const data = await res.json(); + + if (panel === "xray1") { + renderXrayTable("xray1", data, syncedTags || []); + $("#xray1-data-count").textContent = data.length; + } else if (panel === "xray2") { + renderXrayTable("xray2", data, syncedTags || []); + $("#xray2-data-count").textContent = data.length; + } + } catch {} +} + +function renderNovavpsTable(connections) { + const tbody = $("#novavps-tbody"); + if (!connections.length) { + tbody.innerHTML = '