From 108cf22daadcecd68f6959b9264c660abca5a1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=91=D0=BE=D0=B1?= =?UTF-8?q?=D1=8B=D1=80=D0=B5=D0=B2?= Date: Tue, 28 Apr 2026 20:13:54 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B0=20?= =?UTF-8?q?=C2=AB=D0=A1=D0=BD=D0=B5=D1=81=D1=82=D0=B8=20=D1=81=20=D1=80?= =?UTF-8?q?=D0=BE=D1=83=D1=82=D0=B5=D1=80=D0=B0=C2=BB=20=E2=80=94=20=D1=83?= =?UTF-8?q?=D0=B4=D0=B0=D0=BB=D1=8F=D0=B5=D1=82=20VPN/=D1=82=D1=83=D0=BD?= =?UTF-8?q?=D0=BD=D0=B5=D0=BB=D1=8C=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20SSH?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit POST /api/routers/{name}/wireguard/uninstall: - killall autossh - rm init.d S50hydravpn, S99hydra_tun - rm hydra_tun, hydra_tk, wg0.conf, wg0.key - ip link delete nwg50-70 Confirm-диалог с описанием что будет удалено. --- server/main.py | 37 +++++++++++++++++++++++++++++++++++++ server/templates/index.html | 20 ++++++++++++++++++-- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/server/main.py b/server/main.py index a9f0485..d8ac629 100644 --- a/server/main.py +++ b/server/main.py @@ -834,3 +834,40 @@ async def wg_remove_peer(name: str, x_admin_password: str = Header("")): _save_wg(data) await asyncio.to_thread(_wg_reload, _wg_server_conf(data)) return {"ok": True} + + +@app.post("/api/routers/{name}/wireguard/uninstall") +async def wg_uninstall(name: str, x_admin_password: str = Header("")): + """Удалить всё установленное сервером с роутера через SSH.""" + _chk(x_admin_password) + rcfg = _get_router_cfg(name) + script = """\ +echo '=== Удаление HydraVPN с роутера ===' + +killall autossh 2>/dev/null && echo 'autossh остановлен' || true + +if [ -f /opt/etc/init.d/S50hydravpn ]; then + /opt/etc/init.d/S50hydravpn stop 2>/dev/null || true + rm -f /opt/etc/init.d/S50hydravpn && echo 'S50hydravpn удалён' +fi + +if [ -f /opt/etc/init.d/S99hydra_tun ]; then + /opt/etc/init.d/S99hydra_tun stop 2>/dev/null || true + rm -f /opt/etc/init.d/S99hydra_tun && echo 'S99hydra_tun удалён' +fi + +rm -f /opt/bin/hydra_tun && echo 'hydra_tun удалён' || true +rm -f /opt/etc/hydra_tk && echo 'SSH-ключ туннеля удалён' || true +rm -f /opt/etc/wireguard/wg0.conf /opt/etc/wireguard/wg0.key 2>/dev/null && echo 'WireGuard конфиг удалён' || true +rm -f /tmp/hydra_tun.log /tmp/rci.jar /tmp/wg_rci.json 2>/dev/null || true + +for i in $(seq 50 70); do + ip link show nwg$i >/dev/null 2>&1 && ip link delete nwg$i && echo "nwg$i удалён" +done + +echo '=== OK: всё удалено ===' +echo 'Нативные WireGuard-интерфейсы (peer-1, HydraVPN...) удали вручную:' +echo ' Интернет → Другие подключения → WireGuard → Удалить подключение' +""" + rc, out, err = await _ssh_on_router(rcfg, script, timeout=60) + return {"ok": rc == 0, "output": (out + err)[:1500]} diff --git a/server/templates/index.html b/server/templates/index.html index 49e36a8..124a916 100644 --- a/server/templates/index.html +++ b/server/templates/index.html @@ -601,7 +601,8 @@ async function renderWgRouters(){ ? `` : ` - `} + + `} `; }).join(''); @@ -628,13 +629,28 @@ async function wgAddPeer(name){ } async function wgRemovePeer(name){ - if(!confirm(`Удалить ${name} из WireGuard VPN?`)) return; + if(!confirm(`Убрать ${name} из VPN (только из списка, файлы на роутере не трогаем)?`)) return; try { await fetch(`/api/routers/${encodeURIComponent(name)}/wireguard`, {method:'DELETE', headers: authHdr()}); await loadWg(); } catch(e){ alert('Ошибка: '+e); } } +async function wgUninstall(name){ + if(!confirm(`Удалить всё установленное сервером с роутера «${name}» через SSH?\n\n• autossh туннель\n• WireGuard файлы и init.d скрипты\n• nwg50+ интерфейсы\n\nНативные WireGuard-интерфейсы (peer-1, HydraVPN) надо удалить вручную в Keenetic UI.`)) return; + const log = document.getElementById('wg-ops-log'); + log.style.display = 'block'; + log.textContent = `Удаляю VPN/туннель с роутера «${name}»...\n`; + try { + const r = await fetch(`/api/routers/${encodeURIComponent(name)}/wireguard/uninstall`, {method:'POST', headers: authHdr()}); + const d = await r.json(); + log.textContent += d.output || ''; + if(!d.ok) log.textContent += '\n❌ Завершилось с ошибкой'; + else log.textContent += '\n✅ Готово'; + log.scrollTop = log.scrollHeight; + } catch(e){ log.textContent += '\nОшибка: '+e; } +} + async function wgDeploy(name){ const log = document.getElementById('wg-ops-log'); log.style.display = 'block';