Добавлен UI диагностики публичного доступа

Этот коммит содержится в:
Виктор
2026-05-07 00:02:37 +09:00
родитель a5b4d2baca
Коммит 8a5768c49f
+72 -23
Просмотреть файл
@@ -68,7 +68,7 @@
<span class="pill">virsh net-dhcp-leases</span> <span class="pill">virsh net-dhcp-leases</span>
</div> </div>
<pre>{{ ctx.nat.leases }}</pre> <pre>{{ ctx.nat.leases }}</pre>
<div class="notice top-space">IP для проброса теперь определяется автоматически: сначала через qemu-guest-agent, затем по DHCP leases и MAC-адресу VM.</div> <div class="notice top-space">IP для проброса определяется автоматически: сначала через qemu-guest-agent, затем по DHCP leases и MAC-адресу VM.</div>
</article> </article>
</section> </section>
@@ -88,17 +88,15 @@
</select> </select>
</label> </label>
<input type="hidden" name="guest_ip" value="auto"> <input type="hidden" name="guest_ip" value="auto">
<div class="notice"> <div class="notice">Внутренний IP VM будет подставлен автоматически. VM должна быть запущена и получить адрес в NAT-сети.</div>
Внутренний IP VM будет подставлен автоматически. VM должна быть запущена и получить адрес в NAT-сети.
</div>
<div class="form-row"> <div class="form-row">
<label> <label>
<span>Внешний порт сервера</span> <span>Внешний порт сервера</span>
<input type="number" name="external_port" min="1" max="65535" placeholder="2222" required> <input type="number" name="external_port" min="1" max="65535" placeholder="80" required>
</label> </label>
<label> <label>
<span>Порт внутри VM</span> <span>Порт внутри VM</span>
<input type="number" name="guest_port" min="1" max="65535" placeholder="22" required> <input type="number" name="guest_port" min="1" max="65535" placeholder="80" required>
</label> </label>
</div> </div>
<div class="form-row"> <div class="form-row">
@@ -111,7 +109,7 @@
</label> </label>
<label> <label>
<span>Заметка</span> <span>Заметка</span>
<input type="text" name="note" placeholder="SSH / HTTP / RDP"> <input type="text" name="note" placeholder="web / ssh / sip">
</label> </label>
</div> </div>
<button class="primary wide">Добавить и применить</button> <button class="primary wide">Добавить и применить</button>
@@ -120,26 +118,77 @@
<article class="card"> <article class="card">
<div class="card-head"> <div class="card-head">
<h2>Libvirt networks</h2> <h2>Диагностика публичного доступа</h2>
<span class="pill">virsh net-list --all</span> <span class="pill">VM → Internet</span>
</div> </div>
<table> <form method="post" action="/network/diagnose" class="form-grid">
<thead><tr><th>Name</th><th>State</th><th>Autostart</th></tr></thead> <label>
<tbody> <span>Виртуальная машина</span>
{% for net in ctx.networks %} <select name="vm_name" required>
<tr> {% for vm in vms %}
<td>{{ net.name }}</td> <option value="{{ vm.name }}">{{ vm.name }} — {{ vm.state }}</option>
<td><span class="status {{ 'ok' if net.state == 'active' else 'warn' }}">{{ net.state }}</span></td> {% endfor %}
<td>{{ net.autostart }}</td> </select>
</tr> </label>
{% else %} <div class="form-row">
<tr><td colspan="3" class="muted">Libvirt-сети не найдены</td></tr> <label>
{% endfor %} <span>Внешний порт</span>
</tbody> <input type="number" name="external_port" min="1" max="65535" value="80" required>
</table> </label>
<label>
<span>Порт внутри VM</span>
<input type="number" name="guest_port" min="1" max="65535" value="80" required>
</label>
</div>
<label>
<span>Протокол</span>
<select name="protocol">
<option value="tcp">tcp</option>
<option value="udp">udp</option>
</select>
</label>
<button class="primary wide">Проверить доступ</button>
</form>
</article> </article>
</section> </section>
{% if diagnostics %}
<section class="card">
<div class="card-head">
<h2>Результат диагностики</h2>
<span class="pill">{{ diagnostics.vm_name }} · {{ diagnostics.protocol }} {{ diagnostics.external_port }} → {{ diagnostics.guest_port }}</span>
</div>
<div class="notice">{{ diagnostics.verdict }}</div>
<div class="meta-grid top-space">
<div><span>VM IP</span><strong>{{ diagnostics.vm_ip or 'not found' }}</strong></div>
<div><span>External interface</span><strong>{{ diagnostics.external_interface }}</strong></div>
<div><span>Mapping</span><strong>{{ diagnostics.external_port }} → {{ diagnostics.guest_port }}</strong></div>
</div>
<table class="top-space">
<thead><tr><th>Проверка</th><th>Статус</th><th>Детали</th></tr></thead>
<tbody>
{% for check in diagnostics.checks %}
<tr>
<td class="strong">{{ check.name }}</td>
<td><span class="status {{ 'ok' if check.ok else 'err' }}">{{ 'OK' if check.ok else 'FAIL' }}</span></td>
<td class="muted">{{ check.detail }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="grid two top-space">
<article>
<div class="label">Проверить входящий трафик</div>
<pre>{{ diagnostics.commands.watch_external }}</pre>
</article>
<article>
<div class="label">Проверить трафик к VM</div>
<pre>{{ diagnostics.commands.watch_internal }}</pre>
</article>
</div>
</section>
{% endif %}
<section class="card"> <section class="card">
<div class="card-head"> <div class="card-head">
<h2>Пробросы портов</h2> <h2>Пробросы портов</h2>