Add GUI for editing Agent WL (IP Whitelist) and RL (Rate Limit)

- Added Edit modal for existing agents
- Can modify IP whitelist (comma-separated, supports CIDR/FQDN)
- Can modify rate limits (per minute and per hour)
- Shows current values in modal
- Cancel/Save workflow with toast notifications
- Click outside modal to close

The agents.html page now has full CRUD for agent settings:
- Create with WL/RL
- Edit WL/RL
- Lock/Unlock/Revoke
This commit is contained in:
James 2026-04-02 01:22:50 -04:00
parent a2cfff8ec2
commit c4350a614e
1 changed files with 79 additions and 1 deletions

View File

@ -71,6 +71,43 @@
<p class="text-muted text-center" style="padding:2rem 0">Loading...</p>
</div>
</div>
<!-- Edit Agent Modal -->
<div id="editModal" class="modal hidden" style="position:fixed;inset:0;background:rgba(0,0,0,0.7);display:flex;align-items:center;justify-content:center;z-index:1000">
<div class="card" style="width:90%;max-width:500px;max-height:90vh;overflow:auto">
<h3 class="modal-title mb-4">Edit Agent Settings</h3>
<input type="hidden" id="editAgentId">
<div class="form-group mb-4">
<label class="form-label" style="font-weight:600">Agent</label>
<div id="editAgentNameDisplay" style="padding:0.5rem 0;color:var(--text)"></div>
</div>
<div class="form-group mb-4">
<label class="form-label" for="editAgentIPs">IP Whitelist (comma-separated)</label>
<input type="text" id="editAgentIPs" class="form-input" placeholder="1.2.3.4, 10.0.0.0/24, home.smith.family">
<p style="color:var(--muted);font-size:0.75rem;margin-top:0.25rem">
Empty = any IP. Supports IPv4, CIDR ranges, FQDNs.
</p>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1.5rem">
<div class="form-group" style="margin-bottom:0">
<label class="form-label" for="editRateMin">Max requests / minute</label>
<input type="number" id="editRateMin" min="1" max="60" class="form-input">
</div>
<div class="form-group" style="margin-bottom:0">
<label class="form-label" for="editRateHour">Max requests / hour</label>
<input type="number" id="editRateHour" min="1" max="1000" class="form-input">
</div>
</div>
<div style="display:flex;gap:0.75rem;justify-content:flex-end">
<button onclick="closeEditModal()" class="btn btn-ghost">Cancel</button>
<button onclick="saveAgentSettings()" class="btn btn-primary">Save Changes</button>
</div>
</div>
</div>
</div>
<!-- Toast -->
@ -139,7 +176,8 @@
'<td><span class="font-mono text-subtle" style="font-size:0.75rem">' + escapeHtml(a.last_ip) + '</span></td>' +
'<td><span class="text-subtle" style="font-size:0.75rem">' + a.rate_limit_minute + '/m ' + a.rate_limit_hour + '/h</span></td>' +
'<td><span class="text-subtle" style="font-size:0.75rem">' + escapeHtml(ips) + '</span></td>' +
'<td style="display:flex;gap:0.375rem;justify-content:flex-end">';
'<td style="display:flex;gap:0.375rem;justify-content:flex-end">' +
'<button onclick="openEditModal(\'' + a.id + '\', \'' + escapeHtml(a.name) + '\', \'' + escapeHtml((a.ip_whitelist || []).join(\', \')) + '\', ' + (a.rate_limit_minute || 5) + ', ' + (a.rate_limit_hour || 10) + ')" class="btn btn-ghost" style="padding:0.25rem 0.625rem;font-size:0.75rem">Edit</button> ';
if (a.status === 'locked') {
html += '<button onclick="unlockAgent(\'' + a.id + '\')" class="btn btn-ghost" style="padding:0.25rem 0.625rem;font-size:0.75rem">Unlock</button>';
@ -257,6 +295,46 @@
loadAgents();
});
// Edit modal functions
function openEditModal(id, name, ips, rateMin, rateHour) {
document.getElementById('editAgentId').value = id;
document.getElementById('editAgentNameDisplay').textContent = name;
document.getElementById('editAgentIPs').value = ips || '';
document.getElementById('editRateMin').value = rateMin || 5;
document.getElementById('editRateHour').value = rateHour || 10;
document.getElementById('editModal').classList.remove('hidden');
}
function closeEditModal() {
document.getElementById('editModal').classList.add('hidden');
}
async function saveAgentSettings() {
var id = document.getElementById('editAgentId').value;
var ipsStr = document.getElementById('editAgentIPs').value.trim();
var ips = ipsStr ? ipsStr.split(',').map(function(s) { return s.trim(); }).filter(Boolean) : [];
var data = {
ip_whitelist: ips,
rate_limit_minute: parseInt(document.getElementById('editRateMin').value) || 5,
rate_limit_hour: parseInt(document.getElementById('editRateHour').value) || 10
};
try {
await api('PUT', '/api/agents/' + id, data);
toast('Agent settings updated');
closeEditModal();
loadAgents();
} catch(e) {
toast('Failed to update agent: ' + e.message, 'error');
}
}
// Close modal on outside click
document.getElementById('editModal').addEventListener('click', function(e) {
if (e.target === this) closeEditModal();
});
// Stateless: check sessionStorage for master key
if (!getL1Bearer()) {
window.location.href = '/app/';