1191 lines
66 KiB
HTML
1191 lines
66 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en" class="dark">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="theme-color" content="#0f172a">
|
|
<title>P2NS Admin Panel</title>
|
|
<link rel="stylesheet" href="/tailwind.css">
|
|
<link rel="stylesheet" href="styles.css">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
|
<style>
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
background: linear-gradient(135deg, #0a0e1a 0%, #0f1419 50%, #1a1f2e 100%);
|
|
background-attachment: fixed;
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
/* Disable body scrolling for fixed-height tabs */
|
|
body.no-scroll {
|
|
overflow: hidden;
|
|
height: 100vh;
|
|
}
|
|
</style>
|
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/xterm.min.js"></script>
|
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/css/xterm.min.css" rel="stylesheet">
|
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
|
|
</head>
|
|
<body style="color: var(--text-primary);">
|
|
<div class="container mx-auto max-w-7xl">
|
|
<div class="flex justify-between items-center mb-8 flex-wrap gap-4">
|
|
<h1 class="text-4xl font-extrabold" style="background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;">P2NS Admin Panel</h1>
|
|
<div id="status-indicator" class="px-4 py-2 rounded-lg"></div>
|
|
</div>
|
|
|
|
<nav class="flex justify-center mb-8 space-x-4 flex-wrap">
|
|
<!-- Core Management -->
|
|
<button onclick="location.hash = 'domains';" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover m-1">Domains</button>
|
|
<button onclick="location.hash = 'host';" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover m-1">Host</button>
|
|
<button onclick="location.hash = 'entries';" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover m-1">Entries</button>
|
|
<button onclick="location.hash = 'peers';" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover m-1">Peers</button>
|
|
<!-- Network -->
|
|
<button onclick="location.hash = 'local-dns';" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover m-1">Local DNS</button>
|
|
<button onclick="location.hash = 'interfaces';" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover m-1">Interfaces</button>
|
|
<!-- Security -->
|
|
<button onclick="location.hash = 'certs';" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover m-1">Certificates</button>
|
|
<!-- Monitoring -->
|
|
<button onclick="location.hash = 'stats';" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover m-1">Stats</button>
|
|
<button onclick="location.hash = 'logs';" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover m-1">Logs</button>
|
|
<!-- Configuration -->
|
|
<button onclick="location.hash = 'backups';" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover m-1">Backups</button>
|
|
<!-- Plugins -->
|
|
<button onclick="location.hash = 'plugins';" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover m-1">Plugins</button>
|
|
<button onclick="location.hash = 'settings';" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover m-1">Settings</button>
|
|
</nav>
|
|
|
|
<div id="domains" class="tab-content hidden flex flex-col" style="height: calc(100vh - 200px); max-height: calc(100vh - 200px);">
|
|
<h2 class="text-2xl font-bold mb-4 flex items-center gap-2 flex-shrink-0" style="color: var(--text-primary);">
|
|
Domains
|
|
<button onclick="openInfoModal('domains')" class="text-sm px-3 py-1 rounded transition-colors" style="background: var(--bg-glass); border: 1px solid var(--border-color); color: var(--text-secondary);" onmouseover="this.style.background='var(--bg-glass-hover)'" onmouseout="this.style.background='var(--bg-glass)'">Info</button>
|
|
</h2>
|
|
<div class="mb-6 flex-shrink-0">
|
|
<input id="search-domains" type="text" placeholder="Search domains..." class="w-full p-3 rounded-lg focus:outline-none transition-all" style="background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary);" onfocus="this.style.borderColor='var(--primary)'; this.style.boxShadow='0 0 0 3px rgba(99, 102, 241, 0.1)'" onblur="this.style.borderColor='var(--border-color)'; this.style.boxShadow='none'" oninput="filterDomains()">
|
|
</div>
|
|
<div class="overflow-x-auto overflow-y-auto flex-1 min-h-0">
|
|
<table class="w-full rounded-lg" style="background: var(--bg-glass); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); border: 1px solid var(--border-color); box-shadow: var(--shadow-lg);">
|
|
<thead class="sticky top-0 z-10" style="background: var(--bg-secondary); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%);">
|
|
<tr>
|
|
<th class="p-3 text-left">Domain</th>
|
|
<th class="p-3 text-left">Hash</th>
|
|
<th class="p-3 text-left">Consensus</th>
|
|
<th class="p-3 text-left">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="domainsTable"></tbody>
|
|
</table>
|
|
<div id="domainsScrollSentinel" class="scroll-sentinel"></div>
|
|
</div>
|
|
<div id="domainsPagination" class="flex justify-center mt-4 space-x-2 flex-shrink-0"></div>
|
|
<div class="mt-4 flex justify-end flex-shrink-0">
|
|
<button onclick="openAddModal()" class="px-6 py-2.5 bg-primary text-white rounded-lg hover:bg-primary-hover transition-all font-medium shadow-md hover:shadow-lg" style="min-width: auto; width: auto;">Add Domain</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="local-dns" class="tab-content hidden flex flex-col" style="height: calc(100vh - 200px); max-height: calc(100vh - 200px);">
|
|
<h2 class="text-2xl font-bold mb-4 flex items-center gap-2 flex-shrink-0">
|
|
Local DNS
|
|
</h2>
|
|
<div class="flex gap-2 mb-4 flex-shrink-0">
|
|
<button onclick="showSubTab('local-dns', 'records')" id="local-dns-subtab-records" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover">DNS Records</button>
|
|
<button onclick="showSubTab('local-dns', 'conflicts')" id="local-dns-subtab-conflicts" class="px-4 py-2 theme-button-info rounded transition-colors">DNS Conflicts</button>
|
|
</div>
|
|
|
|
<!-- DNS Records Sub-tab -->
|
|
<div id="local-dns-records" class="sub-tab-content flex flex-col flex-1 min-h-0">
|
|
<div class="mb-4 flex items-center gap-2 flex-shrink-0">
|
|
<h3 class="text-xl font-bold">Custom Local DNS Records</h3>
|
|
<button onclick="openInfoModal('local-dns')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</div>
|
|
<div class="mb-6 flex-shrink-0">
|
|
<input id="search-local-dns" type="text" placeholder="Search records..." class="w-full p-3 rounded-lg theme-input focus:outline-none focus:ring-2 focus:ring-primary" oninput="filterLocalDNS()">
|
|
</div>
|
|
<div class="overflow-x-auto overflow-y-auto flex-1 min-h-0">
|
|
<table class="w-full theme-table rounded-lg">
|
|
<thead class="theme-table thead sticky top-0 z-10">
|
|
<tr>
|
|
<th class="p-3 text-left">Name</th>
|
|
<th class="p-3 text-left">Type</th>
|
|
<th class="p-3 text-left">Value</th>
|
|
<th class="p-3 text-left">TTL</th>
|
|
<th class="p-3 text-left">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="localDnsTable"></tbody>
|
|
</table>
|
|
<div id="localDnsScrollSentinel" class="scroll-sentinel"></div>
|
|
</div>
|
|
<div id="localDnsPagination" class="flex justify-center mt-4 space-x-2 flex-shrink-0"></div>
|
|
<div class="mt-4 flex justify-end flex-shrink-0">
|
|
<button onclick="openLocalDnsModal()" class="px-6 py-2.5 bg-primary text-white rounded-lg hover:bg-primary-hover transition-all font-medium shadow-md hover:shadow-lg" style="min-width: auto; width: auto;">Add Record</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DNS Conflicts Sub-tab -->
|
|
<div id="local-dns-conflicts" class="sub-tab-content hidden flex flex-col flex-1 min-h-0">
|
|
<div class="mb-4 flex items-center gap-2 flex-shrink-0">
|
|
<h3 class="text-xl font-bold">DNS Conflict Selector</h3>
|
|
<button onclick="openInfoModal('dns-conflicts')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</div>
|
|
<div class="mb-6 flex-shrink-0">
|
|
<input id="search-dns-conflicts" type="text" placeholder="Search conflicts..." class="w-full p-3 rounded-lg theme-input focus:outline-none focus:ring-2 focus:ring-primary" oninput="filterDnsConflicts()">
|
|
</div>
|
|
<div class="overflow-x-auto overflow-y-auto flex-1 min-h-0">
|
|
<table class="w-full theme-table rounded-lg">
|
|
<thead class="theme-table thead sticky top-0 z-10">
|
|
<tr>
|
|
<th class="p-3 text-left">Domain</th>
|
|
<th class="p-3 text-left">Public IP</th>
|
|
<th class="p-3 text-left">Mode</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="dnsConflictsTable"></tbody>
|
|
</table>
|
|
<div id="dnsConflictsScrollSentinel" class="scroll-sentinel"></div>
|
|
</div>
|
|
<div id="dnsConflictsPagination" class="flex justify-center mt-4 space-x-2 flex-shrink-0"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="entries" class="tab-content hidden flex flex-col" style="height: calc(100vh - 250px); max-height: calc(100vh - 250px);">
|
|
<h2 class="text-2xl font-bold mb-4 flex items-center gap-2 flex-shrink-0">
|
|
Autopass Entries (<span id="entries-count">0</span>)
|
|
<button onclick="openInfoModal('entries')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</h2>
|
|
<div class="mb-6 flex-shrink-0">
|
|
<input id="search-entries" type="text" placeholder="Search entries..." class="w-full p-3 rounded-lg theme-input focus:outline-none focus:ring-2 focus:ring-primary" oninput="filterEntries()">
|
|
</div>
|
|
<div class="overflow-x-auto overflow-y-auto flex-1 min-h-0">
|
|
<table class="w-full theme-table rounded-lg">
|
|
<thead class="entries-sticky-header theme-table thead sticky top-0 z-10">
|
|
<tr>
|
|
<th class="p-3 text-left">Key</th>
|
|
<th class="p-3 text-left">Value</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="entriesTable"></tbody>
|
|
</table>
|
|
<div id="entriesScrollSentinel" style="height: 1px;"></div>
|
|
</div>
|
|
<div id="entriesPagination" class="hidden"></div>
|
|
</div>
|
|
|
|
<div id="peers" class="tab-content hidden flex flex-col" style="height: calc(100vh - 200px); max-height: calc(100vh - 200px);">
|
|
<h2 class="text-2xl font-bold mb-4 flex items-center gap-2 flex-shrink-0">
|
|
Connected Peers <span id="peers-count" class="text-lg"></span>
|
|
<button onclick="openInfoModal('peers')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</h2>
|
|
<div class="mb-6 flex-shrink-0">
|
|
<input id="search-peers" type="text" placeholder="Search peers..." class="w-full p-3 rounded-lg theme-input focus:outline-none focus:ring-2 focus:ring-primary" oninput="filterPeers()">
|
|
</div>
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 flex-1 min-h-0 overflow-hidden items-start">
|
|
<div class="lg:col-span-2 flex flex-col min-h-0 overflow-hidden" style="height: 100%;">
|
|
<div class="overflow-y-auto flex-1 min-h-0">
|
|
<ul id="peersList" class="space-y-3"></ul>
|
|
<div id="peersScrollSentinel" class="scroll-sentinel"></div>
|
|
</div>
|
|
<div id="peersPagination" class="flex justify-center mt-4 space-x-2 flex-shrink-0"></div>
|
|
</div>
|
|
<div class="theme-table rounded-lg p-4" style="height: fit-content;">
|
|
<h3 class="text-lg font-semibold mb-4">Peer Statistics</h3>
|
|
<div style="height: 300px;">
|
|
<canvas id="peer-graph-chart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="certs" class="tab-content hidden flex flex-col" style="height: calc(100vh - 200px); max-height: calc(100vh - 200px);">
|
|
<h2 class="text-2xl font-bold mb-4 flex items-center gap-2 flex-shrink-0">
|
|
Domain Certificates
|
|
<button onclick="openInfoModal('certs')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</h2>
|
|
<div class="mb-6 flex-shrink-0">
|
|
<input id="search-certs" type="text" placeholder="Search certificates..." class="w-full p-3 rounded-lg theme-input focus:outline-none focus:ring-2 focus:ring-primary" oninput="filterCerts()">
|
|
</div>
|
|
<div class="overflow-y-auto flex-1 min-h-0">
|
|
<ul id="certsList" class="space-y-3"></ul>
|
|
<div id="certsScrollSentinel" class="scroll-sentinel"></div>
|
|
</div>
|
|
<div id="certsPagination" class="flex justify-center mt-4 space-x-2 flex-shrink-0"></div>
|
|
<div class="mt-4 flex-shrink-0">
|
|
<input id="cert-domain" placeholder="Domain for Cert" class="p-3 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<button onclick="generateCert()" class="ml-2 px-4 py-3 bg-primary text-white rounded-lg hover:bg-primary-hover">Generate Cert</button>
|
|
</div>
|
|
<h2 class="text-2xl font-bold mt-8 mb-4 flex items-center gap-2 flex-shrink-0">
|
|
CA Management
|
|
<button onclick="openInfoModal('ca-management')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</h2>
|
|
<div class="flex-shrink-0">
|
|
<button onclick="regenerateCA()" class="px-6 py-3 bg-primary text-white rounded-lg hover:bg-primary-hover mr-4">Regenerate Root CA</button>
|
|
<button onclick="installCA()" class="px-6 py-3 bg-primary text-white rounded-lg hover:bg-primary-hover">Install Root CA</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="interfaces" class="tab-content hidden flex flex-col" style="height: calc(100vh - 200px); max-height: calc(100vh - 200px);">
|
|
<h2 class="text-2xl font-bold mb-4 flex items-center gap-2 flex-shrink-0">
|
|
Virtual Interfaces
|
|
<button onclick="openInfoModal('interfaces')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</h2>
|
|
<div class="mb-6 flex-shrink-0">
|
|
<input id="search-interfaces" type="text" placeholder="Search interfaces..." class="w-full p-3 rounded-lg theme-input focus:outline-none focus:ring-2 focus:ring-primary" oninput="filterInterfaces()">
|
|
</div>
|
|
<div class="flex-1 overflow-hidden flex flex-col min-h-0">
|
|
<div class="overflow-x-auto overflow-y-auto flex-1 min-h-0">
|
|
<table class="w-full theme-table rounded-lg">
|
|
<thead class="theme-table thead sticky top-0 z-10">
|
|
<tr>
|
|
<th class="p-3 text-left">Domain</th>
|
|
<th class="p-3 text-left">IP</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="interfacesTable"></tbody>
|
|
</table>
|
|
<div id="interfacesScrollSentinel" class="scroll-sentinel"></div>
|
|
</div>
|
|
<div id="interfacesPagination" class="flex justify-center mt-4 space-x-2 flex-shrink-0"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="logs" class="tab-content hidden" style="height: calc(100vh - 200px); max-height: calc(100vh - 200px);">
|
|
<h2 class="text-2xl font-bold mb-4 flex items-center gap-2">
|
|
Logs
|
|
<button onclick="openInfoModal('logs')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</h2>
|
|
<div id="terminal" class="bg-black rounded-lg overflow-hidden h-96"></div>
|
|
</div>
|
|
|
|
<div id="host" class="tab-content hidden flex flex-col" style="height: calc(100vh - 200px); max-height: calc(100vh - 200px);">
|
|
<h2 class="text-2xl font-bold mb-4 flex items-center gap-2 flex-shrink-0">
|
|
Holesail
|
|
</h2>
|
|
<div class="flex gap-2 mb-4 flex-shrink-0">
|
|
<button onclick="showSubTab('host', 'servers')" id="host-subtab-servers" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover">Servers</button>
|
|
<button onclick="showSubTab('host', 'clients')" id="host-subtab-clients" class="px-4 py-2 theme-button-info rounded transition-colors">Clients</button>
|
|
</div>
|
|
|
|
<!-- Servers Sub-tab -->
|
|
<div id="host-servers" class="sub-tab-content flex flex-col flex-1 min-h-0">
|
|
<div class="mb-4 flex items-center gap-2 flex-shrink-0">
|
|
<h3 class="text-xl font-bold">Holesail Servers</h3>
|
|
<button onclick="openInfoModal('holesail-servers')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</div>
|
|
<div class="mb-6 flex-shrink-0">
|
|
<input id="search-holesail" type="text" placeholder="Search servers..." class="w-full p-3 rounded-lg theme-input focus:outline-none focus:ring-2 focus:ring-primary" oninput="filterHolesailServers()">
|
|
</div>
|
|
<div class="overflow-x-auto overflow-y-auto flex-1 min-h-0">
|
|
<table class="w-full theme-table rounded-lg">
|
|
<thead class="theme-table thead sticky top-0 z-10">
|
|
<tr>
|
|
<th class="p-3 text-left">Name/ID</th>
|
|
<th class="p-3 text-left">Port</th>
|
|
<th class="p-3 text-left">Host</th>
|
|
<th class="p-3 text-left">URL</th>
|
|
<th class="p-3 text-left">Protocol</th>
|
|
<th class="p-3 text-left">Status</th>
|
|
<th class="p-3 text-left">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="holesailTable"></tbody>
|
|
</table>
|
|
<div id="holesailScrollSentinel" class="scroll-sentinel"></div>
|
|
</div>
|
|
<div id="holesailPagination" class="flex justify-center mt-4 space-x-2 flex-shrink-0"></div>
|
|
<div class="mt-4 flex justify-end flex-shrink-0">
|
|
<button onclick="openCreateHolesailModal()" class="px-6 py-2.5 bg-primary text-white rounded-lg hover:bg-primary-hover transition-all font-medium shadow-md hover:shadow-lg" style="min-width: auto; width: auto;">Create Server</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Clients Sub-tab -->
|
|
<div id="host-clients" class="sub-tab-content hidden flex flex-col flex-1 min-h-0">
|
|
<div class="mb-4 flex items-center gap-2 flex-shrink-0">
|
|
<h3 class="text-xl font-bold">Holesail Clients</h3>
|
|
<button onclick="openInfoModal('holesail-clients')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</div>
|
|
<div class="mb-6 flex-shrink-0">
|
|
<input id="search-holesail-clients" type="text" placeholder="Search clients..." class="w-full p-3 rounded-lg theme-input focus:outline-none focus:ring-2 focus:ring-primary" oninput="filterHolesailClients()">
|
|
</div>
|
|
<div class="overflow-x-auto overflow-y-auto flex-1 min-h-0">
|
|
<table class="w-full theme-table rounded-lg">
|
|
<thead class="theme-table thead sticky top-0 z-10">
|
|
<tr>
|
|
<th class="p-3 text-left">Domain</th>
|
|
<th class="p-3 text-left">Key</th>
|
|
<th class="p-3 text-left">Port</th>
|
|
<th class="p-3 text-left">Protocol</th>
|
|
<th class="p-3 text-left">Status</th>
|
|
<th class="p-3 text-left">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="holesailClientsTable"></tbody>
|
|
</table>
|
|
<div id="holesailClientsScrollSentinel" class="scroll-sentinel"></div>
|
|
</div>
|
|
<div id="holesailClientsPagination" class="flex justify-center mt-4 space-x-2 flex-shrink-0"></div>
|
|
<div class="flex gap-3 mt-4 justify-end flex-shrink-0">
|
|
<button onclick="openCreateClientModal()" class="px-6 py-2.5 bg-primary text-white rounded-lg hover:bg-primary-hover transition-all font-medium shadow-md hover:shadow-lg" style="min-width: auto; width: auto;">Create Client</button>
|
|
<button onclick="openServiceSubscriptionModal()" class="px-6 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-all font-medium shadow-md hover:shadow-lg" style="min-width: auto; width: auto;">Service Subscription</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="settings" class="tab-content hidden">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h2 class="text-2xl font-bold flex items-center gap-2">
|
|
Settings
|
|
<button onclick="openInfoModal('settings')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</h2>
|
|
<button id="save-settings-btn" onclick="saveSettings(this)" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover">Save Settings</button>
|
|
</div>
|
|
<div class="mb-6">
|
|
<input id="search-settings" type="text" placeholder="Search settings..." class="w-full p-3 rounded-lg theme-input focus:outline-none focus:ring-2 focus:ring-primary" oninput="filterSettings()">
|
|
</div>
|
|
<div id="settingsContainer" class="space-y-6"></div>
|
|
</div>
|
|
|
|
<div id="plugins" class="tab-content hidden">
|
|
<h2 class="text-2xl font-bold mb-4 flex items-center gap-2">
|
|
Plugins
|
|
<button onclick="openInfoModal('plugins')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</h2>
|
|
<div class="mb-6">
|
|
<input id="search-plugins" type="text" placeholder="Search plugins..." class="w-full p-3 rounded-lg theme-input focus:outline-none focus:ring-2 focus:ring-primary" oninput="filterPlugins()">
|
|
</div>
|
|
<div id="pluginsContainer" class="space-y-6"></div>
|
|
</div>
|
|
|
|
<div id="backups" class="tab-content hidden flex flex-col" style="height: calc(100vh - 200px); max-height: calc(100vh - 200px);">
|
|
<h2 class="text-2xl font-bold mb-4 flex items-center gap-2 flex-shrink-0">
|
|
Backup & Restore
|
|
<button onclick="openInfoModal('backups')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</h2>
|
|
<div class="mb-6 flex-shrink-0">
|
|
<input id="search-backups" type="text" placeholder="Search backups..." class="w-full p-3 rounded-lg theme-input focus:outline-none focus:ring-2 focus:ring-primary" oninput="filterBackups()">
|
|
</div>
|
|
<div class="overflow-x-auto overflow-y-auto flex-1 min-h-0">
|
|
<table class="w-full theme-table rounded-lg">
|
|
<thead class="theme-table thead sticky top-0 z-10">
|
|
<tr>
|
|
<th class="p-3 text-left">Name</th>
|
|
<th class="p-3 text-left">Timestamp</th>
|
|
<th class="p-3 text-left">Size</th>
|
|
<th class="p-3 text-left">Files</th>
|
|
<th class="p-3 text-left">Version</th>
|
|
<th class="p-3 text-left">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="backupsTable"></tbody>
|
|
</table>
|
|
</div>
|
|
<div id="backupsPagination" class="flex justify-center mt-4 space-x-2 flex-shrink-0"></div>
|
|
<button id="create-backup-btn" onclick="createBackup(this)" class="mt-4 px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover flex-shrink-0">Create Backup</button>
|
|
</div>
|
|
|
|
<div id="stats" class="tab-content hidden">
|
|
<div class="flex justify-between items-center mb-6">
|
|
<h2 class="text-2xl font-bold flex items-center gap-2">
|
|
Statistics & Metrics
|
|
<button onclick="openInfoModal('stats')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</h2>
|
|
<div class="flex items-center space-x-4">
|
|
<label class="flex items-center">
|
|
<input type="checkbox" id="auto-refresh-stats" checked class="mr-2">
|
|
<span>Auto-refresh:</span>
|
|
</label>
|
|
<select id="refresh-interval-selector" class="px-3 py-2 theme-input rounded-lg">
|
|
<option value="1000">Realtime (1s)</option>
|
|
<option value="2000">Fast (2s)</option>
|
|
<option value="5000" selected>Normal (5s)</option>
|
|
<option value="10000">Slow (10s)</option>
|
|
<option value="30000">Very Slow (30s)</option>
|
|
</select>
|
|
<select id="time-range-selector" class="px-3 py-2 theme-input rounded-lg">
|
|
<option value="60">Last 1 hour</option>
|
|
<option value="360">Last 6 hours</option>
|
|
<option value="1440" selected>Last 24 hours</option>
|
|
</select>
|
|
<button onclick="exportStats()" class="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">Export Data</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Health Status Overview -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
|
<div class="theme-table rounded-lg p-4">
|
|
<h3 class="text-sm font-semibold theme-text-tertiary mb-2">Overall Status</h3>
|
|
<p id="health-status" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="theme-table rounded-lg p-4">
|
|
<h3 class="text-sm font-semibold theme-text-tertiary mb-2">System Uptime</h3>
|
|
<p id="health-uptime" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Service Status Cards -->
|
|
<div class="mb-6">
|
|
<h3 class="text-xl font-bold mb-4">Service Status</h3>
|
|
<div id="health-services" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"></div>
|
|
</div>
|
|
|
|
<!-- System Overview Cards -->
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
|
<div class="theme-table rounded-lg p-4 overflow-hidden min-h-[140px] flex flex-col">
|
|
<h3 class="text-sm font-semibold theme-text-tertiary mb-2">Node Type</h3>
|
|
<p id="node-type-display" class="text-2xl font-bold mb-1 whitespace-nowrap">-</p>
|
|
<p id="connection-status" class="text-sm theme-text-secondary whitespace-nowrap">-</p>
|
|
</div>
|
|
<div class="theme-table rounded-lg p-4 overflow-hidden min-h-[140px] flex flex-col">
|
|
<h3 class="text-sm font-semibold theme-text-tertiary mb-2">Connected Peers</h3>
|
|
<p id="peers-current" class="text-2xl font-bold whitespace-nowrap">-</p>
|
|
</div>
|
|
<div class="theme-table rounded-lg p-4 overflow-hidden min-h-[140px] flex flex-col">
|
|
<h3 class="text-sm font-semibold theme-text-tertiary mb-2">Total Domains</h3>
|
|
<p id="domains-current" class="text-2xl font-bold whitespace-nowrap">-</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DNS Statistics Section -->
|
|
<div class="theme-table rounded-lg p-6 mb-6">
|
|
<h3 class="text-xl font-bold mb-4">DNS Statistics</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-4">
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Total Queries</p>
|
|
<p id="dns-queries-total" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Success Rate</p>
|
|
<p id="dns-success-rate" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Avg Response Time</p>
|
|
<p id="dns-avg-response" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">P2P Rate</p>
|
|
<p id="dns-p2p-rate" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
<div>
|
|
<canvas id="dns-queries-chart"></canvas>
|
|
</div>
|
|
<div>
|
|
<canvas id="dns-types-chart"></canvas>
|
|
</div>
|
|
</div>
|
|
<div class="mt-4">
|
|
<h4 class="text-lg font-semibold mb-2">Top Queried Domains</h4>
|
|
<div id="top-domains-list" class="space-y-2"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Network & Peers Section -->
|
|
<div class="theme-table rounded-lg p-6 mb-6">
|
|
<h3 class="text-xl font-bold mb-4">Network & Peers</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Connected</p>
|
|
<p id="peers-connected-count" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Total Connections</p>
|
|
<p id="peers-total-connections" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Avg Duration</p>
|
|
<p id="peers-avg-duration" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<canvas id="peer-events-chart"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Holesail Statistics Section -->
|
|
<div class="theme-table rounded-lg p-6 mb-6">
|
|
<h3 class="text-xl font-bold mb-4">Holesail Statistics</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-4">
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Active Connections</p>
|
|
<p id="holesail-active" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Clients Started</p>
|
|
<p id="holesail-clients-started" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Servers Started</p>
|
|
<p id="holesail-servers-started" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Avg Duration</p>
|
|
<p id="holesail-avg-duration" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
<div>
|
|
<canvas id="holesail-events-chart"></canvas>
|
|
</div>
|
|
<div>
|
|
<canvas id="holesail-protocol-chart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Holesail Children Section -->
|
|
<div class="theme-table rounded-lg p-6 mb-6">
|
|
<h3 class="text-xl font-bold mb-4">Holesail Children</h3>
|
|
<div id="holesail-children-container" class="space-y-4">
|
|
<p class="theme-text-tertiary text-center">Loading children data...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- API & Requests Section -->
|
|
<div class="theme-table rounded-lg p-6 mb-6">
|
|
<h3 class="text-xl font-bold mb-4">API & Requests</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Total Requests</p>
|
|
<p id="requests-total" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Success Rate</p>
|
|
<p id="requests-success-rate" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Avg Response Time</p>
|
|
<p id="requests-avg-response" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Failed Requests</p>
|
|
<p id="requests-failed" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
<div>
|
|
<canvas id="requests-timeline-chart"></canvas>
|
|
</div>
|
|
<div>
|
|
<canvas id="endpoint-usage-chart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Resource Usage Section -->
|
|
<div class="theme-table rounded-lg p-6 mb-6">
|
|
<h3 class="text-xl font-bold mb-4">Resource Usage</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Active Sockets</p>
|
|
<p id="resources-sockets" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Active Servers</p>
|
|
<p id="resources-servers" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Holesail Connections</p>
|
|
<p id="resources-holesails" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Peer Channels</p>
|
|
<p id="resources-channels" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<canvas id="resources-timeline-chart"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Peer Channels Section -->
|
|
<div class="theme-table rounded-lg p-6 mb-6">
|
|
<h3 class="text-xl font-bold mb-4">Peer Channels</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Total Plugins</p>
|
|
<p id="peer-channels-total-plugins" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Total Protocols</p>
|
|
<p id="peer-channels-total-protocols" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Open Channels</p>
|
|
<p id="peer-channels-open" class="text-2xl font-bold text-green-500">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Closed/Connecting</p>
|
|
<p id="peer-channels-closed" class="text-2xl font-bold text-yellow-500">-</p>
|
|
</div>
|
|
</div>
|
|
<div id="peer-channels-details" class="space-y-4">
|
|
<p class="theme-text-tertiary text-center">Loading peer channels data...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- HyperDB Databases Section -->
|
|
<div class="theme-table rounded-lg p-6 mb-6">
|
|
<h3 class="text-xl font-bold mb-4">HyperDB Databases</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Total Stores</p>
|
|
<p id="hyperdb-total-stores" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Open Databases</p>
|
|
<p id="hyperdb-total-databases" class="text-2xl font-bold text-green-500">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Replication Active</p>
|
|
<p id="hyperdb-replication-active" class="text-2xl font-bold text-blue-500">-</p>
|
|
</div>
|
|
</div>
|
|
<div id="hyperdb-details" class="space-y-4">
|
|
<p class="theme-text-tertiary text-center">Loading HyperDB data...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Hyperdrive Section -->
|
|
<div class="theme-table rounded-lg p-6 mb-6">
|
|
<h3 class="text-xl font-bold mb-4">Hyperdrive Storage</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Total Drives</p>
|
|
<p id="hyperdrive-total-drives" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Replication Active</p>
|
|
<p id="hyperdrive-replication-active" class="text-2xl font-bold text-blue-500">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Writable Drives</p>
|
|
<p id="hyperdrive-writable" class="text-2xl font-bold text-green-500">-</p>
|
|
</div>
|
|
</div>
|
|
<div id="hyperdrive-details" class="space-y-4">
|
|
<p class="theme-text-tertiary text-center">Loading Hyperdrive data...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Domain Management Section -->
|
|
<div class="theme-table rounded-lg p-6 mb-6">
|
|
<h3 class="text-xl font-bold mb-4">Domain Management</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Total Added</p>
|
|
<p id="domains-added" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Total Removed</p>
|
|
<p id="domains-removed" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Net Change</p>
|
|
<p id="domains-net" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<canvas id="domain-events-chart"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Process Usage Section -->
|
|
<div class="theme-table rounded-lg p-6 mb-6">
|
|
<h3 class="text-xl font-bold mb-4">Process Usage Statistics</h3>
|
|
|
|
<!-- Process Info Cards -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
|
<div class="theme-glass rounded-lg p-4">
|
|
<h4 class="text-sm font-semibold theme-text-tertiary mb-2">Node.js Version</h4>
|
|
<p id="process-node-version" class="text-lg font-bold">-</p>
|
|
</div>
|
|
<div class="theme-glass rounded-lg p-4">
|
|
<h4 class="text-sm font-semibold theme-text-tertiary mb-2">Platform</h4>
|
|
<p id="process-platform" class="text-lg font-bold">-</p>
|
|
</div>
|
|
<div class="theme-glass rounded-lg p-4">
|
|
<h4 class="text-sm font-semibold theme-text-tertiary mb-2">Architecture</h4>
|
|
<p id="process-arch" class="text-lg font-bold">-</p>
|
|
</div>
|
|
<div class="theme-glass rounded-lg p-4">
|
|
<h4 class="text-sm font-semibold theme-text-tertiary mb-2">Process Uptime</h4>
|
|
<p id="process-uptime" class="text-lg font-bold">-</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Memory Usage -->
|
|
<div class="mb-6">
|
|
<h4 class="text-lg font-semibold mb-4">Memory Usage</h4>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4 mb-4">
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Heap Used</p>
|
|
<p id="process-memory-heap-used" class="text-xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Heap Total</p>
|
|
<p id="process-memory-heap-total" class="text-xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">RSS</p>
|
|
<p id="process-memory-rss" class="text-xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">External</p>
|
|
<p id="process-memory-external" class="text-xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Array Buffers</p>
|
|
<p id="process-memory-array-buffers" class="text-xl font-bold">-</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<canvas id="process-memory-chart"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- CPU Usage -->
|
|
<div class="mb-6">
|
|
<h4 class="text-lg font-semibold mb-4">CPU Usage</h4>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Current CPU %</p>
|
|
<p id="process-cpu-current" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Average CPU %</p>
|
|
<p id="process-cpu-avg" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Active Handles</p>
|
|
<p id="process-handles-active" class="text-2xl font-bold">-</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<canvas id="process-cpu-chart"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- System Resources -->
|
|
<div class="mb-6">
|
|
<h4 class="text-lg font-semibold mb-4">System Resources</h4>
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Total Memory</p>
|
|
<p id="process-system-total-memory" class="text-xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Free Memory</p>
|
|
<p id="process-system-free-memory" class="text-xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Used Memory %</p>
|
|
<p id="process-system-used-percent" class="text-xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Load Average</p>
|
|
<p id="process-system-load-avg" class="text-xl font-bold">-</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<canvas id="process-system-chart"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Event Loop Performance -->
|
|
<div class="mb-6">
|
|
<h4 class="text-lg font-semibold mb-4">Event Loop Performance</h4>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Current Lag</p>
|
|
<p id="process-eventloop-current" class="text-xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Average Lag</p>
|
|
<p id="process-eventloop-avg" class="text-xl font-bold">-</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-sm theme-text-tertiary">Max Lag</p>
|
|
<p id="process-eventloop-max" class="text-xl font-bold">-</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<canvas id="process-eventloop-chart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Network Diagnostics -->
|
|
<div class="theme-table rounded-lg p-6 mb-6">
|
|
<h3 class="text-xl font-bold mb-4 flex items-center gap-2">
|
|
Network Diagnostics
|
|
<button onclick="openInfoModal('diagnostics')" class="text-sm px-3 py-1 theme-button-info rounded transition-colors">Info</button>
|
|
</h3>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
|
<!-- DNS Lookup -->
|
|
<div class="theme-glass rounded-lg p-4">
|
|
<h4 class="text-lg font-semibold mb-4">DNS Lookup</h4>
|
|
<div class="space-y-3">
|
|
<input id="dns-lookup-domain" type="text" placeholder="Domain name" class="w-full p-3 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<select id="dns-lookup-type" class="w-full p-3 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<option value="A">A</option>
|
|
<option value="AAAA">AAAA</option>
|
|
<option value="MX">MX</option>
|
|
<option value="TXT">TXT</option>
|
|
<option value="NS">NS</option>
|
|
<option value="CNAME">CNAME</option>
|
|
<option value="SRV">SRV</option>
|
|
<option value="PTR">PTR</option>
|
|
<option value="SOA">SOA</option>
|
|
</select>
|
|
<button onclick="runDnsLookup()" class="w-full px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover">Lookup</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Ping -->
|
|
<div class="theme-glass rounded-lg p-4">
|
|
<h4 class="text-lg font-semibold mb-4">Ping</h4>
|
|
<div class="space-y-3">
|
|
<input id="ping-target" type="text" placeholder="Target (domain or IP)" class="w-full p-3 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<input id="ping-count" type="number" placeholder="Count (default: 4)" value="4" min="1" max="10" class="w-full p-3 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<button onclick="runPing()" class="w-full px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover">Ping</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Traceroute -->
|
|
<div class="theme-glass rounded-lg p-4">
|
|
<h4 class="text-lg font-semibold mb-4">Traceroute</h4>
|
|
<div class="space-y-3">
|
|
<input id="traceroute-target" type="text" placeholder="Target (domain or IP)" class="w-full p-3 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<div class="h-12"></div>
|
|
<button onclick="runTraceroute()" class="w-full px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover">Traceroute</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Connection Test -->
|
|
<div class="theme-glass rounded-lg p-4">
|
|
<h4 class="text-lg font-semibold mb-4">Connection Test</h4>
|
|
<div class="space-y-3">
|
|
<input id="connection-domain" type="text" placeholder="Domain" class="w-full p-3 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<input id="connection-port" type="number" placeholder="Port" min="1" max="65535" class="w-full p-3 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<button onclick="testConnection()" class="w-full px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover">Test Connection</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Invite Diagnostics -->
|
|
<div class="theme-glass rounded-lg p-4 col-span-1 lg:col-span-2">
|
|
<h4 class="text-lg font-semibold mb-4">Invite Diagnostics</h4>
|
|
<div class="space-y-3">
|
|
<p class="text-sm text-gray-400">Diagnose invite system issues including channel health, peer connections, and pending acknowledgments.</p>
|
|
<div class="flex space-x-2">
|
|
<button onclick="runInviteDiagnostics()" class="flex-1 px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover">Run Invite Diagnostics</button>
|
|
<button onclick="cleanDnsPassStorage()" class="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700">🗑️ Clean & Restart</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Results -->
|
|
<div class="mb-6">
|
|
<h4 class="text-lg font-semibold mb-4">Results</h4>
|
|
<div id="diagnostics-results" class="space-y-2"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<dialog id="addDomainModal" class="p-6 theme-glass rounded-lg shadow-xl w-full max-w-md">
|
|
<h3 class="text-xl font-bold mb-4">Add New Domain</h3>
|
|
<input id="modal-domain" placeholder="Domain" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<input id="modal-hash" placeholder="Hash" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<label class="flex items-center mb-4">
|
|
<input id="modal-ssl" type="checkbox" class="mr-2">
|
|
<span>This Holesail Connection Uses SSL/TLS</span>
|
|
</label>
|
|
<div class="flex justify-end space-x-2">
|
|
<button onclick="document.getElementById('addDomainModal').close()" class="px-4 py-2 theme-button-info theme-text-primary rounded theme-glass-hover">Cancel</button>
|
|
<button onclick="submitAddDomain()" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover">Add</button>
|
|
</div>
|
|
</dialog>
|
|
|
|
<dialog id="localDnsModal" class="p-6 theme-glass rounded-lg shadow-xl w-full max-w-md">
|
|
<h3 id="local-dns-title" class="text-xl font-bold mb-4">Add Local DNS Record</h3>
|
|
<input id="local-name" placeholder="Name (domain)" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<select id="local-type" onchange="updateLocalForm()" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<option value="A">A</option>
|
|
<option value="AAAA">AAAA</option>
|
|
<option value="CNAME">CNAME</option>
|
|
<option value="MX">MX</option>
|
|
<option value="TXT">TXT</option>
|
|
<option value="SRV">SRV</option>
|
|
<option value="SOA">SOA</option>
|
|
<option value="CAA">CAA</option>
|
|
<option value="NS">NS</option>
|
|
<option value="PTR">PTR</option>
|
|
<option value="OTHER">OTHER</option>
|
|
</select>
|
|
<div id="local-value-fields" class="mb-4"></div>
|
|
<input id="local-ttl" type="number" placeholder="TTL" value="3600" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<div class="flex justify-end space-x-2">
|
|
<button onclick="document.getElementById('localDnsModal').close()" class="px-4 py-2 theme-button-info theme-text-primary rounded theme-glass-hover">Cancel</button>
|
|
<button id="local-submit" onclick="submitLocalDns(this)" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover">Add</button>
|
|
</div>
|
|
</dialog>
|
|
|
|
<dialog id="certDetailsModal" class="p-6 theme-glass rounded-lg shadow-xl w-full max-w-4xl max-h-[90vh] overflow-hidden flex flex-col">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h3 class="text-2xl font-bold theme-text-primary">Certificate Details</h3>
|
|
<button onclick="document.getElementById('certDetailsModal').close()" class="theme-text-tertiary hover:text-gray-700 dark:hover:text-gray-200 text-3xl leading-none font-bold">×</button>
|
|
</div>
|
|
<div class="flex-1 overflow-y-auto mb-4">
|
|
<div class="rounded-lg p-4 theme-input" style="background: rgba(15, 20, 30, 0.6); backdrop-filter: blur(25px) saturate(200%); -webkit-backdrop-filter: blur(25px) saturate(200%);">
|
|
<pre id="cert-details-content" class="whitespace-pre-wrap break-all text-sm font-mono theme-text-primary leading-relaxed"></pre>
|
|
</div>
|
|
</div>
|
|
<div class="flex justify-end space-x-2 pt-4 border-t border-color: var(--border-color);">
|
|
<button onclick="copyCertDetails()" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded transition-colors">Copy</button>
|
|
<button onclick="document.getElementById('certDetailsModal').close()" class="px-4 py-2 theme-button-info theme-text-primary rounded theme-glass-hover transition-colors">Close</button>
|
|
</div>
|
|
</dialog>
|
|
|
|
<!-- Confirmation modal is created dynamically by confirmation-modal.js -->
|
|
|
|
<dialog id="createHolesailModal" class="p-6 theme-glass rounded-lg shadow-xl w-full max-w-md">
|
|
<h3 class="text-xl font-bold mb-4">Create Holesail Server</h3>
|
|
<input id="holesail-name" placeholder="Name (optional)" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<input id="holesail-port" type="number" placeholder="Port (required)" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<input id="holesail-host" placeholder="Host (default 0.0.0.0)" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<input id="holesail-key" placeholder="Key (optional)" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<input id="holesail-domain" placeholder="Assign to domain (optional)" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<label class="flex items-center mb-4"><input id="holesail-secure" type="checkbox" class="mr-2"> Secure</label>
|
|
<label class="flex items-center mb-4"><input id="holesail-udp" type="checkbox" checked class="mr-2"> UDP</label>
|
|
<select id="holesail-log" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<option value="false">No Logs</option>
|
|
<option value="0">Debug</option>
|
|
<option value="1" selected>Info</option>
|
|
<option value="2">Warn</option>
|
|
<option value="3">Error</option>
|
|
</select>
|
|
<div class="flex justify-end space-x-2">
|
|
<button onclick="document.getElementById('createHolesailModal').close()" class="px-4 py-2 theme-button-info theme-text-primary rounded theme-glass-hover">Cancel</button>
|
|
<button id="create-holesail-btn" onclick="submitCreateHolesail()" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover">Create</button>
|
|
</div>
|
|
</dialog>
|
|
|
|
<dialog id="createClientModal" class="p-6 theme-glass rounded-lg shadow-xl w-full max-w-md">
|
|
<h3 class="text-xl font-bold mb-4">Create Holesail Client</h3>
|
|
<select id="client-domain" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary"></select>
|
|
<input id="client-service-name" placeholder="Service Name (required, e.g., web, api)" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<input id="client-key" placeholder="Key (required)" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<input id="client-port" type="number" placeholder="Local Port (required)" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<select id="client-protocol" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<option value="tcp" selected>TCP</option>
|
|
<option value="udp">UDP</option>
|
|
</select>
|
|
<div class="flex justify-end space-x-2">
|
|
<button onclick="document.getElementById('createClientModal').close()" class="px-4 py-2 theme-button-info theme-text-primary rounded theme-glass-hover">Cancel</button>
|
|
<button id="create-client-btn" onclick="submitCreateClient()" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover">Create</button>
|
|
</div>
|
|
</dialog>
|
|
|
|
<dialog id="serviceSubscriptionModal" class="p-6 theme-glass rounded-lg shadow-xl w-full max-w-4xl max-h-[90vh] overflow-y-auto">
|
|
<h3 class="text-xl font-bold mb-4">Service Subscription</h3>
|
|
<div class="mb-4">
|
|
<input id="subscription-search" type="text" placeholder="Search domains..." class="w-full p-3 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary" oninput="filterSubscriptionDomains()">
|
|
</div>
|
|
<div id="subscription-domains-list" class="space-y-4">
|
|
<p class="theme-text-tertiary">Loading domains...</p>
|
|
</div>
|
|
<div class="flex justify-end space-x-2 mt-4">
|
|
<button onclick="document.getElementById('serviceSubscriptionModal').close()" class="px-4 py-2 theme-button-info theme-text-primary rounded theme-glass-hover">Close</button>
|
|
</div>
|
|
</dialog>
|
|
|
|
<dialog id="holesailLogModal" class="p-6 theme-glass rounded-lg shadow-xl w-full max-w-4xl">
|
|
<h3 id="holesail-log-title" class="text-xl font-bold mb-4">Holesail Logs</h3>
|
|
<div id="holesail-terminal" class="bg-black rounded-lg overflow-hidden h-96"></div>
|
|
<div class="flex justify-end mt-4">
|
|
<button onclick="document.getElementById('holesailLogModal').close()" class="px-4 py-2 theme-button-info theme-text-primary rounded theme-glass-hover">Close</button>
|
|
</div>
|
|
</dialog>
|
|
|
|
<dialog id="infoModal" class="p-6 theme-glass rounded-lg shadow-xl w-full max-w-3xl max-h-[90vh] overflow-y-auto">
|
|
<div class="flex justify-between items-start mb-4">
|
|
<h3 id="info-modal-title" class="text-2xl font-bold"></h3>
|
|
<button onclick="document.getElementById('infoModal').close()" class="theme-text-tertiary hover:text-gray-700 dark:hover:text-gray-200 text-2xl leading-none">×</button>
|
|
</div>
|
|
<div id="info-modal-content" class="mb-6 space-y-4"></div>
|
|
<div class="flex justify-end space-x-2">
|
|
<button onclick="document.getElementById('infoModal').close()" class="px-4 py-2 theme-button-info theme-text-primary rounded theme-glass-hover">Close</button>
|
|
</div>
|
|
</dialog>
|
|
|
|
<dialog id="backupDetailsModal" class="p-6 theme-glass rounded-lg shadow-xl w-full max-w-3xl max-h-[90vh] overflow-y-auto">
|
|
<div class="flex justify-between items-start mb-4">
|
|
<h3 class="text-2xl font-bold">Backup Details</h3>
|
|
<button onclick="document.getElementById('backupDetailsModal').close()" class="theme-text-tertiary hover:text-gray-700 dark:hover:text-gray-200 text-2xl leading-none">×</button>
|
|
</div>
|
|
<div id="backup-details-content" class="mb-6"></div>
|
|
<div class="flex justify-end space-x-2">
|
|
<button onclick="document.getElementById('backupDetailsModal').close()" class="px-4 py-2 theme-button-info theme-text-primary rounded theme-glass-hover">Close</button>
|
|
</div>
|
|
</dialog>
|
|
|
|
<dialog id="peerDetailsModal" class="p-6 theme-glass rounded-lg shadow-xl w-full max-w-3xl max-h-[90vh] overflow-y-auto">
|
|
<div class="flex justify-between items-start mb-4">
|
|
<h3 class="text-2xl font-bold">Peer Details</h3>
|
|
<button onclick="document.getElementById('peerDetailsModal').close()" class="theme-text-tertiary hover:text-gray-700 dark:hover:text-gray-200 text-2xl leading-none">×</button>
|
|
</div>
|
|
<div id="peer-details-content" class="mb-6"></div>
|
|
<div class="flex justify-end space-x-2">
|
|
<button onclick="document.getElementById('peerDetailsModal').close()" class="px-4 py-2 theme-button-info theme-text-primary rounded theme-glass-hover">Close</button>
|
|
</div>
|
|
</dialog>
|
|
|
|
<dialog id="resetIdentityModal" class="p-6 theme-glass rounded-lg shadow-xl w-full max-w-md">
|
|
<h3 class="text-xl font-bold mb-4 text-red-600 dark:text-red-400">⚠️ Reset Identity</h3>
|
|
<p class="text-sm theme-text-primary mb-3">
|
|
<strong>Warning:</strong> This action will permanently delete your current identity keypair (<code class="theme-glass px-1 rounded">cache/keypair.json</code>) and generate a new one.
|
|
</p>
|
|
<div class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-3 mb-3">
|
|
<p class="text-sm text-red-800 dark:text-red-300 font-semibold mb-2">⚠️ This action cannot be undone!</p>
|
|
<ul class="text-xs text-red-700 dark:text-red-400 list-disc list-inside space-y-1">
|
|
<li>You will lose access to all domains you have claimed</li>
|
|
<li>Your claims will become unrecoverable unless network consensus allows you to reclaim them</li>
|
|
<li>Your votes will no longer be associated with your new identity</li>
|
|
<li>You will need to rejoin the network with your new identity</li>
|
|
</ul>
|
|
</div>
|
|
<p class="text-sm theme-text-primary mb-3">
|
|
To confirm, please type <strong>"I, Understand"</strong> (case-sensitive):
|
|
</p>
|
|
<input
|
|
id="reset-identity-confirm"
|
|
type="text"
|
|
placeholder="Type: I, Understand"
|
|
class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-red-500"
|
|
oninput="onResetIdentityConfirmChange()"
|
|
>
|
|
<div class="flex justify-end space-x-2">
|
|
<button
|
|
onclick="document.getElementById('resetIdentityModal').close()"
|
|
class="px-4 py-2 theme-button-info theme-text-primary rounded theme-glass-hover"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
id="reset-identity-submit"
|
|
onclick="resetIdentity(this)"
|
|
disabled
|
|
class="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
Reset Identity
|
|
</button>
|
|
</div>
|
|
</dialog>
|
|
|
|
<dialog id="subnetModal" class="p-6 theme-glass rounded-lg shadow-xl w-full max-w-md">
|
|
<h3 id="subnet-modal-title" class="text-xl font-bold mb-4">Add Subnet</h3>
|
|
|
|
<div class="mb-4">
|
|
<label for="subnet-name" class="block text-sm font-medium theme-text-primary mb-2">Subnet Name</label>
|
|
<input id="subnet-name" placeholder="e.g., Primary Subnet" class="w-full p-3 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<p class="text-xs theme-text-tertiary mt-1">A descriptive name for this subnet</p>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="subnet-base" class="block text-sm font-medium theme-text-primary mb-2">Base IP Address</label>
|
|
<input id="subnet-base" placeholder="e.g., 192.168.3.0" class="w-full p-3 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<p class="text-xs theme-text-tertiary mt-1">The network address of the subnet (typically ends in .0)</p>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="subnet-cidr" class="block text-sm font-medium theme-text-primary mb-2">CIDR Notation</label>
|
|
<input id="subnet-cidr" type="number" min="1" max="32" placeholder="24" value="24" class="w-full p-3 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<p class="text-xs theme-text-tertiary mt-1">Subnet mask in CIDR notation (1-32). /24 = 255.255.255.0 (254 usable IPs)</p>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="subnet-startIndex" class="block text-sm font-medium theme-text-primary mb-2">Start IP Index</label>
|
|
<input id="subnet-startIndex" type="number" min="1" max="254" placeholder="2" value="2" class="w-full p-3 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<p class="text-xs theme-text-tertiary mt-1">First usable IP address index (1-254). IPs will be assigned starting from this number</p>
|
|
</div>
|
|
|
|
<div class="flex justify-end space-x-2">
|
|
<button onclick="document.getElementById('subnetModal').close()" class="px-4 py-2 theme-button-info theme-text-primary rounded theme-glass-hover">Cancel</button>
|
|
<button id="subnet-submit" onclick="submitSubnet(this)" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover">Add</button>
|
|
</div>
|
|
</dialog>
|
|
|
|
<dialog id="dnsServerModal" class="p-6 theme-glass rounded-lg shadow-xl max-w-md" style="width: 100%; max-width: 28rem;">
|
|
<h3 id="dns-server-modal-title" class="text-xl font-bold mb-4">Add Server</h3>
|
|
<input id="dns-server-ip" placeholder="DNS Server IP (e.g., 1.1.1.1)" class="w-full p-3 mb-4 theme-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary">
|
|
<div class="flex justify-end space-x-2">
|
|
<button onclick="document.getElementById('dnsServerModal').close()" class="px-4 py-2 theme-button-info theme-text-primary rounded theme-glass-hover">Cancel</button>
|
|
<button id="dns-server-submit" onclick="submitDnsServer(this)" class="px-4 py-2 bg-primary text-white rounded hover:bg-primary-hover">Add</button>
|
|
</div>
|
|
</dialog>
|
|
</div>
|
|
|
|
<div id="notifications" class="fixed bottom-4 right-4 flex flex-col-reverse space-y-2 z-50"></div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/xterm.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/@xterm/[email protected]/lib/addon-fit.js"></script>
|
|
<!-- Load modules in order: config -> state -> utils -> core -> notifications -> ws-client -> ui modules -> main -->
|
|
<script src="ui/config.js"></script>
|
|
<script src="ui/state.js"></script>
|
|
<script src="utils.js"></script>
|
|
<script src="ui/core.js"></script>
|
|
<script src="ui/notifications.js"></script>
|
|
<script src="ui/confirmation-modal.js"></script>
|
|
<script src="ws-client.js"></script>
|
|
<script src="ui/domains.js"></script>
|
|
<script src="ui/certs.js"></script>
|
|
<script src="ui/interfaces.js"></script>
|
|
<script src="ui/local-dns.js"></script>
|
|
<script src="ui/logs.js"></script>
|
|
<script src="ui/settings.js"></script>
|
|
<script src="ui/stats.js"></script>
|
|
<script src="ui/holesail.js"></script>
|
|
<script src="ui/info-modal.js"></script>
|
|
<script src="ui/system.js"></script>
|
|
<script src="ui/backups.js"></script>
|
|
<script src="ui/health.js"></script>
|
|
<script src="ui/diagnostics.js"></script>
|
|
<script src="ui/peers.js"></script>
|
|
<script src="ui/plugins.js"></script>
|
|
<script src="admin.js"></script>
|
|
<script>
|
|
function handleHashChange() {
|
|
var tabId = location.hash.substring(1);
|
|
if (tabId && document.getElementById(tabId) && typeof showTab === 'function') {
|
|
showTab(tabId);
|
|
}
|
|
}
|
|
window.addEventListener('hashchange', handleHashChange);
|
|
window.addEventListener('load', function() {
|
|
// Wait for admin.js to load
|
|
if (typeof showTab === 'function') {
|
|
var tabId = location.hash.substring(1);
|
|
if (tabId && document.getElementById(tabId)) {
|
|
showTab(tabId);
|
|
} else {
|
|
showTab('domains');
|
|
}
|
|
} else {
|
|
// Retry after a short delay if script hasn't loaded
|
|
setTimeout(function() {
|
|
if (typeof showTab === 'function') {
|
|
var tabId = location.hash.substring(1);
|
|
if (tabId && document.getElementById(tabId)) {
|
|
showTab(tabId);
|
|
} else {
|
|
showTab('domains');
|
|
}
|
|
}
|
|
}, 100);
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |