e2f56b8cc8
Flask WebUI behind nginx reverse proxy with zone-based firewall, DHCP, WireGuard, and ACME certificate management.
91 lines
4.0 KiB
HTML
91 lines
4.0 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Zones - Vacuum Wall{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="page-header">
|
|
<div>
|
|
<h1>Zones</h1>
|
|
<div class="subtitle">Firewalld zone management</div>
|
|
</div>
|
|
<button class="btn btn-primary" onclick="openModal('create-zone-modal')">+ Create Zone</button>
|
|
</div>
|
|
|
|
<div class="card-grid">
|
|
{% for zone in (zones or []) %}
|
|
<div class="card" style="position:relative;">
|
|
<div style="display:flex;justify-content:space-between;align-items:flex-start;">
|
|
<div>
|
|
<h3 style="font-size:16px;color:var(--accent);">{{ zone.get('name', 'unnamed') }}</h3>
|
|
<div class="text-muted text-sm" style="margin-bottom:10px;">
|
|
{% if zone.get('target') %}Target: {{ zone.target }}{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="text-sm mb-4">
|
|
<div class="text-muted" style="margin-bottom:4px;">Interfaces</div>
|
|
{% if zone.get('interfaces') %}
|
|
{% for iface in zone.interfaces %}
|
|
<span class="badge badge-info">{{ iface }}</span>
|
|
{% endfor %}
|
|
{% else %}
|
|
<span class="text-muted">None</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="text-sm mb-4">
|
|
<div class="text-muted" style="margin-bottom:4px;">Services</div>
|
|
{% if zone.get('services') %}
|
|
{% for svc in zone.services %}
|
|
<span class="badge badge-success">{{ svc }}</span>
|
|
{% endfor %}
|
|
{% else %}
|
|
<span class="text-muted">None</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div style="display:flex;justify-content:flex-end;gap:6px;margin-top:12px;">
|
|
<form method="POST" action="/api/firewall/zones/{{ zone.get('name', '') }}/delete" hx-post="/api/firewall/zones/{{ zone.get('name', '') }}/delete" hx-swap="none" onsubmit="return confirm('Delete zone {{ zone.name }}? This will affect traffic to its interfaces.');" class="htmx-on-success" data-success="Zone deleted">
|
|
<button type="submit" class="btn btn-sm btn-danger">Delete</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% if not (zones or []) %}
|
|
<div class="card">
|
|
<div class="text-muted text-sm">No zones configured. Create a zone to get started.</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Create Zone Modal -->
|
|
<div class="modal-overlay" id="create-zone-modal" onclick="if(event.target===this) closeModal('create-zone-modal')">
|
|
<div class="modal">
|
|
<h2>Create Zone</h2>
|
|
<form hx-post="/api/firewall/zones" hx-swap="none" class="htmx-on-success" data-success="Zone created" onsuccess="setTimeout(function(){closeModal('create-zone-modal');},500); location.reload();">
|
|
<div class="form-group">
|
|
<label for="zone-name">Zone Name</label>
|
|
<input type="text" id="zone-name" name="name" placeholder="e.g., trusted, dmz, external" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="zone-target">Target</label>
|
|
<select id="zone-target" name="target">
|
|
<option value="default">default</option>
|
|
<option value="%%REJECT%%">%REJECT%</option>
|
|
<option value="%%DROP%%">%DROP%</option>
|
|
<option value="%%ACCEPT%%">%ACCEPT%</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="zone-services">Default Services (comma-separated)</label>
|
|
<input type="text" id="zone-services" name="services" placeholder="e.g., dhcp, dns, ssh">
|
|
</div>
|
|
<div class="modal-actions">
|
|
<button type="button" class="btn btn-outline" onclick="closeModal('create-zone-modal')">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Create</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|