- Rename app title to SUB-ALIGATOR (header, page titles, exports, README) - Add dark amber-terminal theme with a header toggle, persisted in localStorage - Add pool deletion (soft delete): allocations are kept in history and block the address space again when the same or an overlapping pool is re-added (re-enable on exact CIDR match, re-attach contained allocations) - Reject new pools that overlap an enabled pool - Move Home link into the page header, keep other links in the footer - Load app.js globally via base template, add theme.js (CSP-safe, no inline JS)
101 lines
4 KiB
HTML
101 lines
4 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Manage pools | SUB-ALIGATOR{% endblock %}
|
|
|
|
{% block content %}
|
|
<section class="box">
|
|
<div class="box-title">Existing pools</div>
|
|
|
|
{% if error != "" %}
|
|
<div class="message error">
|
|
<p>{{ error }}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if pools.len() == 0 %}
|
|
<p>No pools configured yet.</p>
|
|
{% else %}
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Slug</th>
|
|
<th>Parent prefix</th>
|
|
<th>Type</th>
|
|
<th>Allowed prefixes</th>
|
|
<th>Allocations (active / reserved / released)</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for p in pools %}
|
|
<tr>
|
|
<td data-label="Name">{{ p.pool.name }}</td>
|
|
<td data-label="Slug">{{ p.pool.slug }}</td>
|
|
<td data-label="Parent prefix">{{ p.pool.cidr }}</td>
|
|
<td data-label="Type">{{ p.pool.kind }}</td>
|
|
<td data-label="Allowed prefixes">{{ p.pool.allowed_prefixes_json }}</td>
|
|
<td data-label="Allocations">{{ p.active_count }} / {{ p.reserved_count }} / {{ p.released_count }}</td>
|
|
<td data-label="Actions">
|
|
<button type="button" class="btn btn-danger pool-delete-btn" data-id="{{ p.pool.id }}">Delete</button>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<section class="box">
|
|
<div class="box-title">Add new pool</div>
|
|
|
|
<form method="post" action="/pools/new">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
|
|
<label for="slug">Pool slug *</label>
|
|
<input type="text" id="slug" name="slug" required maxlength="50" pattern="[a-z0-9-]+" placeholder="e.g. vpn">
|
|
<p class="small">Short machine identifier used in URLs and API paths (e.g. <code>vpn</code>). Lowercase letters, numbers and hyphens only.</p>
|
|
|
|
<label for="name">Pool name *</label>
|
|
<input type="text" id="name" name="name" required minlength="2" maxlength="100" placeholder="e.g. VPN subnets">
|
|
|
|
<label for="cidr">Parent prefix (CIDR) *</label>
|
|
<input type="text" id="cidr" name="cidr" required placeholder="e.g. 10.200.0.0/16">
|
|
<p class="small">If you re-add a range that was deleted before, its previous allocations are kept and will block the address space again.</p>
|
|
|
|
<label for="kind">Type *</label>
|
|
<input type="text" id="kind" name="kind" required maxlength="50" placeholder="e.g. VPN">
|
|
|
|
<label for="allowed_prefixes">Allowed prefix lengths *</label>
|
|
<input type="text" id="allowed_prefixes" name="allowed_prefixes" required value="20,21,22,23,24,25,26,27,28,29,30" placeholder="e.g. 24,25,26">
|
|
<p class="small">Comma-separated list of prefix lengths. Each must be equal to or larger than the pool prefix.</p>
|
|
|
|
<div style="margin-top: 1rem;">
|
|
<button type="submit" class="btn">Add pool</button>
|
|
<a href="/" class="btn btn-secondary">Back</a>
|
|
</div>
|
|
</form>
|
|
</section>
|
|
|
|
{% for p in pools %}
|
|
<div id="delete-pool-modal-{{ p.pool.id }}" class="modal-overlay" style="display:none;">
|
|
<div class="modal">
|
|
<h2 class="modal-title">Delete pool</h2>
|
|
<form method="post" action="/pools/{{ p.pool.id }}/delete" class="modal-body">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<p>Pool: <strong>{{ p.pool.name }}</strong> ({{ p.pool.cidr }})</p>
|
|
{% if p.total_count > 0 %}
|
|
<p>This pool has {{ p.active_count }} active, {{ p.reserved_count }} reserved and {{ p.released_count }} released allocations.</p>
|
|
<p><strong>Allocations will NOT be deleted.</strong> They stay in history and will keep blocking this address range if you later add the same or an overlapping pool again.</p>
|
|
{% else %}
|
|
<p>This pool has no allocations.</p>
|
|
{% endif %}
|
|
<div class="modal-actions">
|
|
<button type="button" class="btn btn-secondary cancel-btn" data-modal="delete-pool-modal-{{ p.pool.id }}">Cancel</button>
|
|
<button type="submit" class="btn btn-danger">Delete pool</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% endblock %}
|