200e078bc5
- Add daemon/ module with aiohttp server, sync client, and handler registry - Add daemon/handlers/ for privileged operations (acme, dnsmasq, firewall, logs, nginx, wireguard) - Add system/acme-deploy.py, vacuum-walld sudoers and systemd service - Update API routes to use daemon client instead of lib/ directly - Update lib/, tests/, and webui/ for new architecture - Update docs and deployment scripts
62 lines
2.6 KiB
HTML
62 lines
2.6 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Interfaces - Vacuum Wall{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="page-header">
|
|
<div>
|
|
<h1>Interfaces</h1>
|
|
<div class="subtitle">Network interface to zone bindings</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Interface</th>
|
|
<th>MAC Address</th>
|
|
<th>IP Address</th>
|
|
<th>State</th>
|
|
<th>Zone</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="interface-list">
|
|
{% for iface in (interfaces or []) %}
|
|
<tr>
|
|
<td><strong>{{ iface.get('display_name', iface.get('name', 'unknown')) }}</strong></td>
|
|
<td class="text-muted">{{ iface.get('mac', 'N/A') }}</td>
|
|
<td>
|
|
{% for ip in iface.get('ips', []) %}
|
|
{{ ip }}{% if not loop.last %}, {% endif %}
|
|
{% endfor %}
|
|
{% if not iface.get('ips') %}N/A{% endif %}
|
|
</td>
|
|
<td>
|
|
<span class="status-dot {{ 'status-up' if iface.get('state') == 'UP' else 'status-down' }}"></span>
|
|
{{ 'Up' if iface.get('state') == 'UP' else 'Down' }}
|
|
</td>
|
|
<td>
|
|
{% if zones %}
|
|
<select
|
|
hx-on::change="fetch('/api/firewall/zones/'+encodeURIComponent(this.value)+'/interfaces',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({interfaces:['{{ iface.name }}']})}).then(r=>{if(!r.ok)throw r}).then(r=>r.ok?(showSuccessToast('{{ iface.display_name }} assigned to '+this.value),refreshTable('/api/firewall/interfaces',document.getElementById('interface-list'),renderInterfaces)):r.json().then(j=>{throw new Error(j.error||r.statusText)})).catch(e=>{showErrorToast(e.message);this.selectedIndex=0})"
|
|
>
|
|
{% for zname in zones %}
|
|
<option value="{{ zname }}" {% if zname == iface.get('zone') %}selected{% endif %}>{{ zname }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
{% else %}
|
|
<span class="text-muted">No zones configured</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
{% if not (interfaces or []) %}
|
|
<tr>
|
|
<td colspan="6" class="text-muted text-sm">No interfaces found</td>
|
|
</tr>
|
|
{% endif %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% endblock %}
|