diff --git a/public/app.js b/public/app.js index 7b3915b..a8df635 100644 --- a/public/app.js +++ b/public/app.js @@ -1454,12 +1454,18 @@ async function loadInstances() { ? `` : ``; const badge = m.badge ? `${m.badge}` : ""; + const srcTag = i.managed + ? "" + : `приложение Amnezia`; + const delBtn = i.managed + ? `` + : ""; return `
${m.icon}
${toggle}
-
${escapeHtmlSafe(m.title)} ${badge}
+
${escapeHtmlSafe(m.title)} ${badge} ${srcTag}
${escapeHtmlSafe(i.variantMeta?.desc || "")}
${status}
@@ -1468,7 +1474,7 @@ async function loadInstances() {
- + ${delBtn}
`; }).join(""); diff --git a/public/styles.css b/public/styles.css index e71e883..4eaefa5 100644 --- a/public/styles.css +++ b/public/styles.css @@ -1201,3 +1201,5 @@ tr:last-child td { .instance-form-row label { font-size: 0.75rem; color: var(--muted); } .instance-hint { font-size: 0.75rem; margin-top: 8px; } .instance-empty { padding: 12px; } + +.inst-src { font-size: 0.6rem; color: var(--muted); border: 1px solid var(--line); border-radius: 999px; padding: 2px 7px; vertical-align: middle; } diff --git a/server.js b/server.js index 140ec5a..66fa2ce 100644 --- a/server.js +++ b/server.js @@ -2653,24 +2653,48 @@ function runInstanceScript(args) { }); } +function detectVariantFromHead(head, iface) { + if (iface === "wg0" || !/^S1\s*=/m.test(head)) return "legacy"; + if (/^S3\s*=/m.test(head) || /^S4\s*=/m.test(head)) return "awg2"; + return "awg"; +} + app.get("/api/instances", requireAuth, async (_req, res) => { - const managed = loadManagedProfiles(); + const managedIds = new Set(loadManagedProfiles().map((m) => m.id)); + const profiles = getProfiles(); const running = await listRunningContainerNames(); const items = []; - for (const m of managed) { + for (const prof of profiles) { + const isManaged = managedIds.has(prof.id) || prof.managed === true; + let container = prof.container; + let runningNow = false; let peers = null; - if (running.includes(m.container)) { - try { - const out = (await execDocker(["exec", m.container, m.wgBinary, "show", m.iface, "peers"])).stdout || ""; - peers = out.split("\n").map((x) => x.trim()).filter(Boolean).length; - } catch { peers = null; } + let port = prof.port || null; + let variant = prof.variant || "awg2"; + try { + const rt = createRuntime(prof); + container = await rt.resolveContainer(); + runningNow = running.includes(container); + if (runningNow) { + const { conf } = await rt.loadState(); + peers = conf.peers.length; + const m = /^ListenPort\s*=\s*(\d+)/m.exec(conf.head); + if (m) port = Number(m[1]); + if (!prof.variant) variant = detectVariantFromHead(conf.head, prof.iface || "awg0"); + } + } catch { + runningNow = container ? running.includes(container) : false; } items.push({ - id: m.id, label: m.label, variant: m.variant, port: m.port, - container: m.container, - running: running.includes(m.container), + id: prof.id, + label: prof.label, + variant, + port, + container, + running: runningNow, peers, - variantMeta: INSTANCE_VARIANTS[m.variant] || null, + managed: isManaged, + variantMeta: INSTANCE_VARIANTS[variant] || null, }); } res.json({ instances: items, variants: INSTANCE_VARIANTS }); @@ -2720,17 +2744,26 @@ app.post("/api/instances/delete", requireAuth, requireProTier, async (req, res) res.json({ ok: true }); }); +async function resolveProfileContainer(id) { + const prof = getProfiles().find((p) => p.id === id); + if (!prof) return null; + try { return await createRuntime(prof).resolveContainer(); } + catch { return prof.container || null; } +} + app.post("/api/instances/stop", requireAuth, requireProTier, async (req, res) => { const id = String(req.body?.id || "").trim(); - if (!loadManagedProfiles().some((p) => p.id === id)) return res.status(404).json({ error: "Инстанс не найден." }); - try { await execDocker(["stop", id]); res.json({ ok: true }); } + const container = await resolveProfileContainer(id); + if (!container) return res.status(404).json({ error: "Инстанс не найден." }); + try { await execDocker(["stop", container]); res.json({ ok: true }); } catch (e) { res.status(500).json({ error: String(e.message || e) }); } }); app.post("/api/instances/start", requireAuth, requireProTier, async (req, res) => { const id = String(req.body?.id || "").trim(); - if (!loadManagedProfiles().some((p) => p.id === id)) return res.status(404).json({ error: "Инстанс не найден." }); - try { await execDocker(["start", id]); res.json({ ok: true }); } + const container = await resolveProfileContainer(id); + if (!container) return res.status(404).json({ error: "Инстанс не найден." }); + try { await execDocker(["start", container]); res.json({ ok: true }); } catch (e) { res.status(500).json({ error: String(e.message || e) }); } });