Settings

Integrations Users Audit Log Client Settings Whitelabel Developer

AWS Route53 Settings

Configure AWS credentials and hosted zones for PTR record management.

Enter the hosted zone IDs for your forward DNS zones (A records)

IP Registry Integration

Enrich IP entries with ISP, organization, and security flag data from ipregistry.co.

Get your API key from ipregistry.co.

n8n Webhook Integration

Configure webhooks to notify n8n when geofeed data changes.

Webhook Queue

User Management

Manage admin and staff users who can access this application. Users are authenticated via Cloudflare Access.

Email Display Name Role Status Created Actions

View all changes made to geofeed entries including creates, updates, and deletes.

Client Shortnames

Client shortnames currently in use across geofeed entries.

Company Logos

Manage logo images for client shortnames. Logos will appear in the entries table as 1:1 square icons.

Branding

Customize the appearance of the application with your company branding.

Displayed in the header and browser tab title
Displayed as subtitle in the header
URL to an SVG or PNG logo for the header
URL to a favicon (.ico, .png, or .svg)
Pre-populated URL when importing from URL

Preview

ISP IP Manager

Import Geofeed Data

Import geofeed entries from a CSV file or a remote URL.

Upload CSV File

Import from URL

System Information

Error Logs

Danger Zone

Irreversible actions. Proceed with caution.

// Initialize settings page document.addEventListener("DOMContentLoaded", function() { const currentTab = "' . $currentTab . '"; // Load data for the current tab switch(currentTab) { case "integrations": loadAwsSettings(); loadIpRegistrySettings(); loadWebhookSettings(); loadWebhookQueueStatus(); break; case "users": loadUsers(); break; case "audit": loadAuditLog(); break; case "advanced": loadShortnamesGrid(); loadShortnames(); loadLogosGrid(); break; case "whitelabel": loadWhitelabelSettings(); break; case "developer": loadSystemInfo(); loadErrorLogs(); break; } }); // User Management Functions async function loadUsers() { try { const response = await fetch("api.php?action=admin_users_list", { headers: { "X-CSRF-Token": CSRF_TOKEN } }); const data = await response.json(); if (!data.success) { throw new Error(data.error || "Failed to load users"); } const tbody = document.getElementById("usersTableBody"); if (data.users.length === 0) { tbody.innerHTML = \'No users found. Add a user above.\'; return; } tbody.innerHTML = data.users.map(user => ` ${escapeHtml(user.email)} ${user.display_name ? escapeHtml(user.display_name) : \'-\'} ${user.role === \'admin\' ? \'Admin\' : \'Staff\'} ${user.active == 1 ? \'Active\' : \'Inactive\'} ${formatDate(user.created_at)}
`).join(\'\'); } catch (error) { console.error("Error loading users:", error); showNotification("Failed to load users: " + error.message, "error"); } } async function addUser() { const email = document.getElementById("newUserEmail").value.trim(); const displayName = document.getElementById("newUserDisplayName").value.trim(); const role = document.getElementById("newUserRole").value; if (!email) { showNotification("Please enter an email address", "error"); return; } try { const response = await fetch("api.php?action=admin_user_save", { method: "POST", headers: { "Content-Type": "application/json", "X-CSRF-Token": CSRF_TOKEN }, body: JSON.stringify({ email: email, display_name: displayName || null, role: role }) }); const data = await response.json(); if (!data.success) { throw new Error(data.error || "Failed to add user"); } showNotification("User added successfully", "success"); document.getElementById("newUserEmail").value = ""; document.getElementById("newUserDisplayName").value = ""; document.getElementById("newUserRole").value = "staff"; loadUsers(); } catch (error) { console.error("Error adding user:", error); showNotification("Failed to add user: " + error.message, "error"); } } async function toggleUser(userId) { try { const response = await fetch("api.php?action=admin_user_toggle", { method: "POST", headers: { "Content-Type": "application/json", "X-CSRF-Token": CSRF_TOKEN }, body: JSON.stringify({ id: userId }) }); const data = await response.json(); if (!data.success) { throw new Error(data.error || "Failed to toggle user status"); } showNotification("User status updated", "success"); loadUsers(); } catch (error) { console.error("Error toggling user:", error); showNotification("Failed to toggle user: " + error.message, "error"); } } async function deleteUser(userId, email) { if (!confirm("Are you sure you want to delete the user \"" + email + "\"? This action cannot be undone.")) { return; } try { const response = await fetch("api.php?action=admin_user_delete", { method: "POST", headers: { "Content-Type": "application/json", "X-CSRF-Token": CSRF_TOKEN }, body: JSON.stringify({ id: userId }) }); const data = await response.json(); if (!data.success) { throw new Error(data.error || "Failed to delete user"); } showNotification("User deleted successfully", "success"); loadUsers(); } catch (error) { console.error("Error deleting user:", error); showNotification("Failed to delete user: " + error.message, "error"); } } function escapeHtml(text) { if (!text) return ""; const div = document.createElement("div"); div.textContent = text; return div.innerHTML; } function formatDate(dateStr) { if (!dateStr) return "-"; const date = new Date(dateStr); return date.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit" }); } '; // Include footer require_once __DIR__ . '/includes/footer.php'; ?>