Show protocol enable state and which nodes have each protocol installed.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
orohi
2026-07-29 04:18:09 +03:00
co-authored by Cursor
parent 6510de0214
commit 1ad5125bf4
12 changed files with 745 additions and 67 deletions
+13 -1
View File
@@ -88,13 +88,25 @@
<article class="proto-card {{if .Enabled}}is-on{{else}}is-off{{end}}">
<div class="proto-top">
<h3>{{.Name}}</h3>
<span class="badge {{if .Enabled}}on{{else}}off{{end}}">{{if .Enabled}}ON{{else}}OFF{{end}}</span>
<span class="badge {{if .Enabled}}on{{else}}off{{end}}">{{.EnabledLabel}}</span>
</div>
<p>{{.Description}}</p>
<div class="proto-meta">
<code>{{.Code}}</code>
<span>порт {{.Port}}</span>
</div>
<div class="proto-servers">
{{if .InstalledOn}}
<span class="stat-label">Серверы</span>
<div class="chip-list">
{{range .InstalledOn}}
<a class="chip {{statusClass (printf "%s" .Status)}}" href="/admin/nodes/{{.NodeID}}">{{.NodeName}}</a>
{{end}}
</div>
{{else}}
<span class="muted">Не установлен ни на одной ноде</span>
{{end}}
</div>
</article>
{{end}}
</div>
+57
View File
@@ -46,12 +46,69 @@
<form method="POST" action="/admin/nodes/{{.Node.ID}}/check">
<button class="btn btn-ghost" type="submit">Проверить online</button>
</form>
<form method="POST" action="/admin/nodes/{{.Node.ID}}/sync">
<button class="btn btn-ghost" type="submit">Синхронизировать протоколы</button>
</form>
<form method="POST" action="/admin/nodes/{{.Node.ID}}/delete" onsubmit="return confirm('Удалить ноду из панели?');">
<button class="btn btn-ghost" type="submit">Удалить</button>
</form>
<a class="btn btn-ghost" href="/admin/nodes">К списку</a>
</div>
<section class="panel-section">
<h2>Протоколы на этом сервере</h2>
<div class="table-wrap">
<table class="data-table">
<thead>
<tr>
<th>Протокол</th>
<th>Порт</th>
<th>Установлен</th>
<th>Включён</th>
<th></th>
</tr>
</thead>
<tbody>
{{range .NodeProtocols}}
<tr>
<td>
<strong>{{.ProtocolName}}</strong>
<div><code>{{.ProtocolCode}}</code></div>
</td>
<td>{{.Port}}</td>
<td><span class="badge {{if .Installed}}on{{else}}off{{end}}">{{if .Installed}}Да{{else}}Нет{{end}}</span></td>
<td><span class="badge {{if .Enabled}}on{{else}}off{{end}}">{{if .Enabled}}ON{{else}}OFF{{end}}</span></td>
<td class="actions actions-multi">
{{if not .Installed}}
<form method="POST" action="/admin/nodes/{{$.Node.ID}}/protocols/{{.ProtocolID}}">
<input type="hidden" name="action" value="install">
<button class="btn btn-sm btn-primary" type="submit">Установить</button>
</form>
{{else}}
{{if .Enabled}}
<form method="POST" action="/admin/nodes/{{$.Node.ID}}/protocols/{{.ProtocolID}}">
<input type="hidden" name="action" value="disable">
<button class="btn btn-sm btn-ghost" type="submit">Выключить</button>
</form>
{{else}}
<form method="POST" action="/admin/nodes/{{$.Node.ID}}/protocols/{{.ProtocolID}}">
<input type="hidden" name="action" value="enable">
<button class="btn btn-sm btn-primary" type="submit">Включить</button>
</form>
{{end}}
<form method="POST" action="/admin/nodes/{{$.Node.ID}}/protocols/{{.ProtocolID}}">
<input type="hidden" name="action" value="uninstall">
<button class="btn btn-sm btn-ghost" type="submit">Снять</button>
</form>
{{end}}
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</section>
<section class="panel-section">
<h2>Важно</h2>
<p class="muted">Откройте порт <strong>{{.Node.NodePort}}</strong> на ноде <em>только</em> для IP панели (как NODE_PORT в Remnawave).</p>
+37 -6
View File
@@ -26,31 +26,57 @@
<main class="admin-main">
<header class="admin-header">
<h1>Протоколы</h1>
<p class="muted">Включение и отключение VPN-протоколов</p>
<p class="muted">Статус в панели и на каких серверах протокол установлен / включён</p>
</header>
{{if .Flash}}<div class="alert ok">{{.Flash}}</div>{{end}}
{{if .Error}}<div class="alert">{{.Error}}</div>{{end}}
<div class="table-wrap">
<table class="data-table">
<thead>
<tr>
<th>Протокол</th>
<th>Код</th>
<th>Порт</th>
<th>Статус</th>
<th>В панели</th>
<th>Установлен на</th>
<th>Включён на</th>
<th></th>
</tr>
</thead>
<tbody>
{{range .Protocols}}
<tr>
<tr class="{{if .Enabled}}row-on{{else}}row-off{{end}}">
<td>
<strong>{{.Name}}</strong>
<div class="cell-desc">{{.Description}}</div>
<code>{{.Code}}</code>
</td>
<td><code>{{.Code}}</code></td>
<td>{{.Port}}</td>
<td>
<span class="badge {{if .Enabled}}on{{else}}off{{end}}">{{if .Enabled}}Включён{{else}}Выключен{{end}}</span>
<span class="badge {{if .Enabled}}on{{else}}off{{end}}">{{.EnabledLabel}}</span>
</td>
<td>
{{if .InstalledOn}}
<div class="chip-list">
{{range .InstalledOn}}
<a class="chip {{statusClass (printf "%s" .Status)}}" href="/admin/nodes/{{.NodeID}}" title="{{.Host}}">{{.NodeName}}</a>
{{end}}
</div>
{{else}}
<span class="muted">нигде</span>
{{end}}
</td>
<td>
{{if .EnabledOn}}
<div class="chip-list">
{{range .EnabledOn}}
<a class="chip on" href="/admin/nodes/{{.NodeID}}" title="{{.Host}}">{{.NodeName}}</a>
{{end}}
</div>
{{else}}
<span class="muted">нигде</span>
{{end}}
</td>
<td class="actions">
<form method="POST" action="/admin/protocols/{{.ID}}/toggle">
@@ -64,6 +90,11 @@
</tbody>
</table>
</div>
<p class="hint" style="margin-top:1rem">
Установка на сервер — на странице ноды. «В панели» = доступен в каталоге;
«Установлен / Включён на» = фактическое состояние на серверах.
</p>
</main>
</body>
</html>