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';