fix: show distinct awg instance labels

This commit is contained in:
andrey271192
2026-06-21 18:34:26 +03:00
parent 8c67d7df93
commit a9a67afb54
5 changed files with 16 additions and 6 deletions

View File

@@ -32,6 +32,8 @@
После переустановки старый `AWG_PROFILES` и сохранённые managed-инстансы могли указывать на один и тот же контейнер/конфиг с разными `id`. Начиная с **v1.2.32** панель дедуплицирует профили по `container + confPath + iface + clientsPath`, а также чистит дубли при чтении/сохранении `instances.json`. Обновите панель из GitHub и сделайте жёсткое обновление страницы. После переустановки старый `AWG_PROFILES` и сохранённые managed-инстансы могли указывать на один и тот же контейнер/конфиг с разными `id`. Начиная с **v1.2.32** панель дедуплицирует профили по `container + confPath + iface + clientsPath`, а также чистит дубли при чтении/сохранении `instances.json`. Обновите панель из GitHub и сделайте жёсткое обновление страницы.
Если проверка на сервере показывает два разных контейнера, например `amnezia-awg2` и `amnezia-awg`, с разными портами и разным числом клиентов — это не дубль, а два инстанса. Начиная с **v1.2.33** карточки используют реальное имя профиля (`label`), чтобы `AmneziaWG 2.0` и `AmneziaWG` не выглядели одинаково.
## Лендинг не поднимается (порт 80 занят) ## Лендинг не поднимается (порт 80 занят)
При ошибке bind `:80` используйте при установке **`SKIP_LANDING=1`** или **`LANDING_PORT=8081`** — админка на `HOST_PORT` (например 8080) от этого не зависит. При ошибке bind `:80` используйте при установке **`SKIP_LANDING=1`** или **`LANDING_PORT=8081`** — админка на `HOST_PORT` (например 8080) от этого не зависит.

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "amnezia-admin", "name": "amnezia-admin",
"version": "1.2.32", "version": "1.2.33",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "amnezia-admin", "name": "amnezia-admin",
"version": "1.2.32", "version": "1.2.33",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"express": "^4.21.2" "express": "^4.21.2"

View File

@@ -1,6 +1,6 @@
{ {
"name": "amnezia-admin", "name": "amnezia-admin",
"version": "1.2.32", "version": "1.2.33",
"private": false, "private": false,
"description": "amnezia_web-PRO — веб-панель AmneziaWG: несколько инстансов, WARP, каскад/Endpoint, экспорт .conf (Docker)", "description": "amnezia_web-PRO — веб-панель AmneziaWG: несколько инстансов, WARP, каскад/Endpoint, экспорт .conf (Docker)",
"license": "MIT", "license": "MIT",

View File

@@ -1482,6 +1482,7 @@ async function loadInstances() {
} }
grid.innerHTML = list.map((i) => { grid.innerHTML = list.map((i) => {
const m = VARIANT_META[i.variant] || { icon: "🛡", title: i.variant, badge: "" }; const m = VARIANT_META[i.variant] || { icon: "🛡", title: i.variant, badge: "" };
const title = i.label || m.title || i.variant || i.id;
const status = i.running const status = i.running
? `<span class="inst-status on">● РАБОТАЕТ</span>` ? `<span class="inst-status on">● РАБОТАЕТ</span>`
: `<span class="inst-status off">● ОСТАНОВЛЕН</span>`; : `<span class="inst-status off">● ОСТАНОВЛЕН</span>`;
@@ -1493,14 +1494,14 @@ async function loadInstances() {
? "" ? ""
: `<span class="inst-src" title="Контейнер развёрнут приложением Amnezia">приложение Amnezia</span>`; : `<span class="inst-src" title="Контейнер развёрнут приложением Amnezia">приложение Amnezia</span>`;
const delBtn = i.managed const delBtn = i.managed
? `<button class="btn small warn" data-act="delete" data-id="${i.id}" data-label="${escapeHtmlSafe(m.title)}">Удалить</button>` ? `<button class="btn small warn" data-act="delete" data-id="${i.id}" data-label="${escapeHtmlSafe(title)}">Удалить</button>`
: ""; : "";
return `<div class="inst-card"> return `<div class="inst-card">
<div class="inst-card-head"> <div class="inst-card-head">
<div class="inst-ico">${m.icon}</div> <div class="inst-ico">${m.icon}</div>
${toggle} ${toggle}
</div> </div>
<div class="inst-title">${escapeHtmlSafe(m.title)} ${badge} ${srcTag}</div> <div class="inst-title">${escapeHtmlSafe(title)} ${badge} ${srcTag}</div>
<div class="inst-desc muted">${escapeHtmlSafe(i.variantMeta?.desc || "")}</div> <div class="inst-desc muted">${escapeHtmlSafe(i.variantMeta?.desc || "")}</div>
<div class="inst-status-row">${status}</div> <div class="inst-status-row">${status}</div>
<div class="inst-stats"> <div class="inst-stats">

View File

@@ -2900,6 +2900,13 @@ function detectVariantFromHead(head, iface) {
return "awg"; return "awg";
} }
function inferVariantFromProfile(prof) {
const raw = `${prof.variant || ""} ${prof.id || ""} ${prof.container || ""} ${prof.label || ""}`.toLowerCase();
if (raw.includes("legacy") || prof.iface === "wg0") return "legacy";
if (raw.includes("awg2") || raw.includes("2.0")) return "awg2";
return "awg";
}
app.get("/api/instances", requireAuth, async (_req, res) => { app.get("/api/instances", requireAuth, async (_req, res) => {
const managedIds = new Set(loadManagedProfiles().map((m) => m.id)); const managedIds = new Set(loadManagedProfiles().map((m) => m.id));
const profiles = getProfiles(); const profiles = getProfiles();
@@ -2912,7 +2919,7 @@ app.get("/api/instances", requireAuth, async (_req, res) => {
let runningNow = false; let runningNow = false;
let peers = null; let peers = null;
let port = prof.port || null; let port = prof.port || null;
let variant = prof.variant || "awg2"; let variant = prof.variant || inferVariantFromProfile(prof);
try { try {
const rt = createRuntime(prof); const rt = createRuntime(prof);
container = await rt.resolveContainer(); container = await rt.resolveContainer();