mirror of
https://github.com/andrey271192/amnezia_web.git
synced 2026-09-20 14:41:59 +00:00
perf(mtproto): fast tg link; logs via separate endpoint
/api/mtproto/status skips docker logs. /api/mtproto/logs loads tail async. Install response sets link card immediately. v1.2.23. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -91,6 +91,8 @@ cd /opt/amnezia-admin && chmod +x scripts/install.sh && sudo SKIP_DOWNLOAD=1 bas
|
||||
| `UI_HIDE_MTPROTO` | _(не задано)_ | `1` или **`UI_HIDE_SECTIONS=...,mtproto`** — скрыть раздел установки MTProto в веб‑интерфейсе (по умолчанию виден даже FREE). |
|
||||
| `FREE_PANEL_CONTAINER_FOR_PRO_INSTALL` и др. | см. `server.js` | Имена контейнеров для `docker rm` перед install (по умолчанию `amnezia-admin`, `amnezia-web-landing`, `amnezia-admin-pro`). |
|
||||
|
||||
Примечание для MTProto на панели: **`GET /api/mtproto/status`** отдаёт состояние и **`tg://` без синхронного `docker logs`**; хвост логов подгружается вторым запросом **`GET /api/mtproto/logs`**, чтобы блок со ссылкой открывался быстрее.
|
||||
|
||||
Чтобы включить форму установки из панели после «обычной» установки без **`INSTALL_FREE…`**, задайте в контейнере **`AMNEZIA_EDITION=community`** и **`ALLOW_COMMUNITY_GITHUB_ACTIVATION=1`** (или проще переустановите с **`INSTALL_FREE_COMMUNITY_ACTIVATION=1`**). URL приватного `install.sh` при необходимости переопределите через **`COMMUNITY_PRIVATE_INSTALL_SCRIPT_URL`**.
|
||||
|
||||
**Важно:** при одноразовом запуске с переменными в одной строке с `curl … | sudo bash` они **пропадают** — нужен **`export`** перед `curl` и **`sudo -E bash`**, см. блок **«Установка для клиента»** выше.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "amnezia-admin",
|
||||
"version": "1.2.22",
|
||||
"version": "1.2.23",
|
||||
"private": true,
|
||||
"description": "amnezia_web — базовая панель AmneziaWG (FREE: просмотр и удаление клиентов; полная версия — PRO).",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -426,6 +426,28 @@ function applyEditionPayload(data) {
|
||||
if (wgRawDetails) wgRawDetails.hidden = uiHidden.users || !editionState.showDebugWg;
|
||||
}
|
||||
|
||||
/** @param {string} [tgLink] */
|
||||
function setMtprotoLinkCard(tgLink) {
|
||||
const t = typeof tgLink === "string" ? tgLink.trim() : "";
|
||||
if (!mtprotoLinkCardEl) return;
|
||||
if (!t) {
|
||||
mtprotoLinkCardEl.classList.add("hidden");
|
||||
delete mtprotoLinkCardEl.dataset.tgLink;
|
||||
if (mtprotoLinkAnchorEl) {
|
||||
mtprotoLinkAnchorEl.removeAttribute("href");
|
||||
mtprotoLinkAnchorEl.textContent = "";
|
||||
}
|
||||
return;
|
||||
}
|
||||
mtprotoLinkCardEl.classList.remove("hidden");
|
||||
mtprotoLinkCardEl.dataset.tgLink = t;
|
||||
if (mtprotoLinkAnchorEl) {
|
||||
mtprotoLinkAnchorEl.href = t;
|
||||
mtprotoLinkAnchorEl.textContent = t;
|
||||
}
|
||||
if (mtprotoLinkCopyState) mtprotoLinkCopyState.textContent = "";
|
||||
}
|
||||
|
||||
async function refreshMtprotoPanel() {
|
||||
if (!mtprotoPanel || !mtprotoStatusLine || !mtprotoActionsEl) return;
|
||||
if (uiHidden.mtproto) {
|
||||
@@ -434,6 +456,7 @@ async function refreshMtprotoPanel() {
|
||||
mtprotoCalloutEl.textContent = "";
|
||||
mtprotoCalloutEl.classList.add("hidden");
|
||||
}
|
||||
setMtprotoLinkCard("");
|
||||
return;
|
||||
}
|
||||
mtprotoPanel.hidden = false;
|
||||
@@ -443,11 +466,17 @@ async function refreshMtprotoPanel() {
|
||||
mtprotoBanner.textContent = "";
|
||||
}
|
||||
try {
|
||||
if (mtprotoLogsTailEl) mtprotoLogsTailEl.textContent = "Загрузка логов…";
|
||||
const snap = await api("/api/mtproto/status");
|
||||
if (mtprotoLogsTailEl) {
|
||||
mtprotoLogsTailEl.textContent =
|
||||
typeof snap.logsTail === "string" ? snap.logsTail : "";
|
||||
}
|
||||
void (async () => {
|
||||
try {
|
||||
const lg = await api("/api/mtproto/logs");
|
||||
const tail = typeof lg?.logsTail === "string" ? lg.logsTail : "";
|
||||
if (mtprotoLogsTailEl) mtprotoLogsTailEl.textContent = tail;
|
||||
} catch {
|
||||
if (mtprotoLogsTailEl) mtprotoLogsTailEl.textContent = "(лог не загрузился — обновите раздел позже)";
|
||||
}
|
||||
})();
|
||||
|
||||
if (mtprotoCalloutEl) {
|
||||
if (snap.exists) {
|
||||
@@ -485,20 +514,7 @@ async function refreshMtprotoPanel() {
|
||||
mtprotoActionsEl.appendChild(imgHint);
|
||||
}
|
||||
|
||||
if (mtprotoLinkCardEl) {
|
||||
if (snap.tgLink) {
|
||||
mtprotoLinkCardEl.classList.remove("hidden");
|
||||
mtprotoLinkCardEl.dataset.tgLink = snap.tgLink;
|
||||
if (mtprotoLinkAnchorEl) {
|
||||
mtprotoLinkAnchorEl.href = snap.tgLink;
|
||||
mtprotoLinkAnchorEl.textContent = snap.tgLink;
|
||||
}
|
||||
if (mtprotoLinkCopyState) mtprotoLinkCopyState.textContent = "";
|
||||
} else {
|
||||
mtprotoLinkCardEl.classList.add("hidden");
|
||||
delete mtprotoLinkCardEl.dataset.tgLink;
|
||||
}
|
||||
}
|
||||
setMtprotoLinkCard(snap.tgLink);
|
||||
|
||||
const row = document.createElement("div");
|
||||
row.className = "warp-actions";
|
||||
@@ -516,6 +532,7 @@ async function refreshMtprotoPanel() {
|
||||
}
|
||||
if (/^[a-f0-9]{32}$/.test(sec)) body.secret = sec;
|
||||
const j = await api("/api/mtproto/install", { method: "POST", body: JSON.stringify(body) });
|
||||
if (typeof j.tgLink === "string" && j.tgLink.trim()) setMtprotoLinkCard(j.tgLink.trim());
|
||||
const lines = [];
|
||||
if (typeof j.secretHex === "string") lines.push(`Секрет (сохраните): ${j.secretHex}`);
|
||||
if (typeof j.tgLink === "string" && j.tgLink) lines.push(`Ссылка: ${j.tgLink}`);
|
||||
@@ -580,10 +597,7 @@ async function refreshMtprotoPanel() {
|
||||
mtprotoCalloutEl.textContent = "";
|
||||
mtprotoCalloutEl.classList.add("hidden");
|
||||
}
|
||||
if (mtprotoLinkCardEl) {
|
||||
mtprotoLinkCardEl.classList.add("hidden");
|
||||
delete mtprotoLinkCardEl.dataset.tgLink;
|
||||
}
|
||||
setMtprotoLinkCard("");
|
||||
if (mtprotoLogsTailEl) mtprotoLogsTailEl.textContent = "";
|
||||
const err = document.createElement("p");
|
||||
err.className = "muted warp-muted err";
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;600;700&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/styles.css?v=1.2.22">
|
||||
<link rel="stylesheet" href="/styles.css?v=1.2.23">
|
||||
<script>
|
||||
(function () {
|
||||
try {
|
||||
@@ -312,6 +312,6 @@
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<script src="/app.js?v=1.2.22" type="module"></script>
|
||||
<script src="/app.js?v=1.2.23" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
17
server.js
17
server.js
@@ -2246,12 +2246,19 @@ app.get("/api/mtproto/status", requireAuth, (req, res) => {
|
||||
return res.status(403).json({ error: MSG_UI_MTProto_OFF });
|
||||
}
|
||||
const snap = mtprotoSnapshot(hostFromRequest(req));
|
||||
let logsTail = "";
|
||||
if (snap.exists && snap.running) {
|
||||
const l = dockerSpawnSync(["logs", "--tail", "100", MTPRO_CONTAINER], 12_000);
|
||||
if (l.code === 0) logsTail = l.stdout.trim().slice(-4500);
|
||||
res.json({ ...snap });
|
||||
});
|
||||
|
||||
app.get("/api/mtproto/logs", requireAuth, (req, res) => {
|
||||
if (effectiveUiHidden().mtproto) {
|
||||
return res.status(403).json({ error: MSG_UI_MTProto_OFF });
|
||||
}
|
||||
res.json({ ...snap, logsTail });
|
||||
const snap = mtprotoSnapshot(hostFromRequest(req));
|
||||
if (!snap.exists || !snap.running) return res.json({ logsTail: "" });
|
||||
const l = dockerSpawnSync(["logs", "--tail", "100", MTPRO_CONTAINER], 12_000);
|
||||
let logsTail = "";
|
||||
if (l.code === 0) logsTail = l.stdout.trim().slice(-4500);
|
||||
res.json({ logsTail });
|
||||
});
|
||||
|
||||
app.post("/api/mtproto/install", requireAuth, (req, res) => {
|
||||
|
||||
Reference in New Issue
Block a user