mirror of
https://github.com/andrey271192/amnezia_web-PRO.git
synced 2026-09-20 14:42:00 +00:00
fix: preserve AWG_PROFILES on reinstall; GET export URL + token
- install.sh: snapshot /root/amnezia-admin.awg-profiles.json + restore from old container - GET /api/clients/export-config (+ profileId); optional EXPORT_CONFIG_SECRET - UI: direct link, copy URL, hint when last_config missing Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -512,6 +512,13 @@ function renderRows(clients) {
|
||||
btn("Переименовать", "btn small ghost", () => void renameClient(c))
|
||||
);
|
||||
nameWrap.append(strong, renameWrap);
|
||||
if (!c.exportAvailable) {
|
||||
const exHint = document.createElement("p");
|
||||
exHint.className = "muted export-missing-hint";
|
||||
exHint.textContent =
|
||||
"Экспорт .conf с сервера недоступен: нет userData.last_config (ключи только в приложении Amnezia).";
|
||||
nameWrap.appendChild(exHint);
|
||||
}
|
||||
nameTd.appendChild(nameWrap);
|
||||
|
||||
const ipTd = document.createElement("td");
|
||||
@@ -549,8 +556,21 @@ function renderRows(clients) {
|
||||
);
|
||||
}
|
||||
if (c.exportAvailable) {
|
||||
const direct = document.createElement("a");
|
||||
direct.className = "btn small ghost";
|
||||
direct.href = clientExportGetUrl(c.clientId);
|
||||
direct.textContent = "Прямая ссылка";
|
||||
direct.rel = "noopener";
|
||||
direct.title =
|
||||
"Открыть в новой вкладке — скачается .conf, если вы авторизованы в этой панели (cookies).";
|
||||
|
||||
actTd.appendChild(btn("Скачать .conf", "btn small ghost", () => void downloadClientConfig(c)));
|
||||
actTd.appendChild(direct);
|
||||
actTd.appendChild(
|
||||
btn("Скачать .conf", "btn small ghost", () => void downloadClientConfig(c)),
|
||||
btn("Копировать URL", "btn small ghost", async () => {
|
||||
const ok = await copyTextToClipboard(clientExportGetUrl(c.clientId));
|
||||
setStatus(ok ? "Ссылка скопирована (вставьте в браузер, будучи залогиненным)." : "Не удалось скопировать.", !ok);
|
||||
}),
|
||||
);
|
||||
}
|
||||
actTd.appendChild(btn("Удалить", "btn small warn", () => confirmDelete(c.name, c.clientId)));
|
||||
@@ -708,14 +728,46 @@ function escapeHtml(s) {
|
||||
.replace(/"/g, """);
|
||||
}
|
||||
|
||||
/** Query для нужного инстанса при нескольких профилях AWG_PROFILES */
|
||||
function currentProfileQuerySuffix() {
|
||||
if (!protoSelect || !protoSwitch || protoSwitch.classList.contains("hidden")) return "";
|
||||
const pid = String(protoSelect.value || "").trim();
|
||||
return pid ? `&profileId=${encodeURIComponent(pid)}` : "";
|
||||
}
|
||||
|
||||
/** Прямая GET-ссылка на скачивание (работает в браузере с активной сессией панели). */
|
||||
function clientExportGetUrl(clientId) {
|
||||
const q = `clientId=${encodeURIComponent(clientId)}${currentProfileQuerySuffix()}`;
|
||||
return `${window.location.origin}/api/clients/export-config?${q}`;
|
||||
}
|
||||
|
||||
async function copyTextToClipboard(text) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
return true;
|
||||
} catch {
|
||||
try {
|
||||
const ta = document.createElement("textarea");
|
||||
ta.value = text;
|
||||
ta.style.position = "fixed";
|
||||
ta.style.left = "-9999px";
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand("copy");
|
||||
ta.remove();
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadClientConfig(c) {
|
||||
try {
|
||||
setStatus("Готовлю конфиг…", false);
|
||||
const res = await fetch("/api/clients/export-config", {
|
||||
method: "POST",
|
||||
const res = await fetch(clientExportGetUrl(c.clientId), {
|
||||
method: "GET",
|
||||
credentials: "same-origin",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ clientId: c.clientId }),
|
||||
});
|
||||
const text = await res.text();
|
||||
if (!res.ok) {
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<div>
|
||||
<p class="eyebrow">Панель сервера</p>
|
||||
<h1>Пользователи AmneziaWG</h1>
|
||||
<p class="sub">Дату отключения задаёте сами (локальное время браузера). Кнопка «Выключить» убирает клиента из туннеля сразу. «Задать дату» без галочки — только запись в таблице; с галочкой — клиент остаётся в туннеле до наступления времени. Несколько инстансов AmneziaWG (Legacy и др.) — в переменной AWG_PROFILES и списке «Инстанс».</p>
|
||||
<p class="sub">Дату отключения задаёте сами (локальное время браузера). Кнопка «Выключить» убирает клиента из туннеля сразу. «Задать дату» без галочки — только запись в таблице; с галочкой — клиент остаётся в туннеле до наступления времени. Несколько инстансов задаются переменной <code class="inline">AWG_PROFILES</code>; установщик при апдейте подставляет её из прежнего контейнера или из <code class="inline">/root/amnezia-admin.awg-profiles.json</code>. Конфиг .conf — только если на сервере есть <code class="inline">last_config</code>; иначе используйте приложение Amnezia.</p>
|
||||
</div>
|
||||
<div class="token-box">
|
||||
<div class="session-actions">
|
||||
|
||||
@@ -521,6 +521,13 @@ tr:last-child td {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.export-missing-hint {
|
||||
margin: 0.35rem 0 0;
|
||||
font-size: 0.72rem;
|
||||
line-height: 1.35;
|
||||
max-width: 22rem;
|
||||
}
|
||||
|
||||
.raw {
|
||||
margin-top: 1.25rem;
|
||||
color: var(--muted);
|
||||
|
||||
Reference in New Issue
Block a user