feat(ui): optional tunnel disable when setting disconnect date

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Андрей Бобырев
2026-05-14 15:55:20 +03:00
parent 5a5fc8b240
commit 7b291ff1d5
3 changed files with 62 additions and 7 deletions

View File

@@ -23,9 +23,12 @@ const dtClientEl = document.querySelector("#dt-dialog-client");
const dtInput = document.querySelector("#dt-dialog-input"); const dtInput = document.querySelector("#dt-dialog-input");
const dtCancel = document.querySelector("#dt-dialog-cancel"); const dtCancel = document.querySelector("#dt-dialog-cancel");
const dtOk = document.querySelector("#dt-dialog-ok"); const dtOk = document.querySelector("#dt-dialog-ok");
const dtExtra = document.querySelector("#dt-dialog-extra");
const dtHint = document.querySelector("#dt-dialog-hint");
const dtAlsoDisable = document.querySelector("#dt-dialog-also-disable");
let dtMode = "disable"; let dtMode = "disable";
/** @type {{ clientId: string, name: string } | null} */ /** @type {Record<string, unknown> | null} */
let dtClient = null; let dtClient = null;
function isoToDatetimeLocal(iso) { function isoToDatetimeLocal(iso) {
@@ -54,6 +57,8 @@ function openDisableDialog(c) {
dtClientEl.textContent = c.name; dtClientEl.textContent = c.name;
dtOk.textContent = "Выключить"; dtOk.textContent = "Выключить";
dtInput.value = isoToDatetimeLocal(new Date().toISOString()); dtInput.value = isoToDatetimeLocal(new Date().toISOString());
dtExtra.classList.add("hidden");
dtAlsoDisable.checked = false;
dtDialog.showModal(); dtDialog.showModal();
} }
@@ -66,6 +71,15 @@ function openEditDisconnectDialog(c) {
const iso = const iso =
c.lastDisconnectedAt || (!c.activeInConf && c.disabledAt) || new Date().toISOString(); c.lastDisconnectedAt || (!c.activeInConf && c.disabledAt) || new Date().toISOString();
dtInput.value = isoToDatetimeLocal(iso); dtInput.value = isoToDatetimeLocal(iso);
if (c.activeInConf) {
dtExtra.classList.remove("hidden");
dtHint.textContent =
"Без галочки меняется только дата в таблице — в туннеле клиент остаётся. Чтобы реально отключить ключ, включите «Выключить из туннеля».";
dtAlsoDisable.checked = false;
} else {
dtExtra.classList.add("hidden");
dtAlsoDisable.checked = false;
}
dtDialog.showModal(); dtDialog.showModal();
} }
@@ -91,12 +105,20 @@ dtOk.addEventListener("click", async () => {
body: JSON.stringify({ clientId: dtClient.clientId, disconnectedAt: iso }), body: JSON.stringify({ clientId: dtClient.clientId, disconnectedAt: iso }),
}); });
} else { } else {
setStatus("Сохраняю дату…", false); const alsoTunnel = Boolean(dtAlsoDisable.checked && dtClient.activeInConf);
setStatus(alsoTunnel ? "Выключаю из туннеля…" : "Сохраняю дату…", false);
if (alsoTunnel) {
await api("/api/clients/disable", {
method: "POST",
body: JSON.stringify({ clientId: dtClient.clientId, disconnectedAt: iso }),
});
} else {
await api("/api/clients/disconnect-date", { await api("/api/clients/disconnect-date", {
method: "POST", method: "POST",
body: JSON.stringify({ clientId: dtClient.clientId, disconnectedAt: iso }), body: JSON.stringify({ clientId: dtClient.clientId, disconnectedAt: iso }),
}); });
} }
}
dtDialog.close(); dtDialog.close();
dtClient = null; dtClient = null;
setStatus("Готово.", false); setStatus("Готово.", false);

View File

@@ -29,7 +29,7 @@
<div> <div>
<p class="eyebrow">Панель сервера</p> <p class="eyebrow">Панель сервера</p>
<h1>Пользователи AmneziaWG</h1> <h1>Пользователи AmneziaWG</h1>
<p class="sub">Дату отключения вы задаёте сами: при нажатии «Выключить» или кнопкой «Задать дату» в колонке «Последнее отключение». Время берётся как локальное на вашем компьютере.</p> <p class="sub">Дату отключения вы задаёте сами: при «Выключить» или в диалоге «Задать дату» (для активных клиентов там можно отметить выключение из туннеля). Время — локальное на вашем компьютере.</p>
</div> </div>
<div class="token-box"> <div class="token-box">
<div class="session-actions"> <div class="session-actions">
@@ -108,6 +108,13 @@
<p class="muted" id="dt-dialog-client"></p> <p class="muted" id="dt-dialog-client"></p>
<label for="dt-dialog-input">Дата и время</label> <label for="dt-dialog-input">Дата и время</label>
<input type="datetime-local" id="dt-dialog-input" step="60"> <input type="datetime-local" id="dt-dialog-input" step="60">
<div id="dt-dialog-extra" class="dt-dialog-extra hidden">
<p class="dt-dialog-hint" id="dt-dialog-hint"></p>
<label class="dt-checkbox-label">
<input type="checkbox" id="dt-dialog-also-disable">
Выключить клиента из туннеля AmneziaWG (убрать из awg0.conf)
</label>
</div>
<div class="dt-dialog-actions"> <div class="dt-dialog-actions">
<button type="button" class="btn ghost" id="dt-dialog-cancel">Отмена</button> <button type="button" class="btn ghost" id="dt-dialog-cancel">Отмена</button>
<button type="button" class="btn primary" id="dt-dialog-ok">Подтвердить</button> <button type="button" class="btn primary" id="dt-dialog-ok">Подтвердить</button>

View File

@@ -443,6 +443,32 @@ tr:last-child td {
font: inherit; font: inherit;
} }
.dt-dialog-extra {
margin-top: 0.85rem;
}
.dt-dialog-hint {
margin: 0 0 0.65rem;
font-size: 0.82rem;
line-height: 1.45;
color: var(--muted);
}
.dt-checkbox-label {
display: flex;
gap: 0.55rem;
align-items: flex-start;
font-size: 0.85rem;
line-height: 1.45;
color: var(--text);
cursor: pointer;
}
.dt-checkbox-label input {
margin-top: 0.15rem;
flex-shrink: 0;
}
.dt-dialog-actions { .dt-dialog-actions {
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;