Spaces:
Paused
Paused
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Virtual ISP Stack - Network Management Dashboard</title> | |
| <link rel="stylesheet" href="styles.css"> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> | |
| </head> | |
| <body> | |
| <div class="app-container"> | |
| <!-- Header --> | |
| <header class="header"> | |
| <div class="header-content"> | |
| <div class="logo"> | |
| <i class="fas fa-network-wired"></i> | |
| <h1>Virtual ISP Stack</h1> | |
| </div> | |
| <div class="header-status"> | |
| <div class="status-indicator" id="systemStatus"> | |
| <i class="fas fa-circle"></i> | |
| <span>System Status</span> | |
| </div> | |
| <div class="refresh-btn" onclick="refreshData()"> | |
| <i class="fas fa-sync-alt"></i> | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Navigation --> | |
| <nav class="sidebar"> | |
| <div class="nav-menu"> | |
| <div class="nav-item active" data-section="dashboard"> | |
| <i class="fas fa-tachometer-alt"></i> | |
| <span>Dashboard</span> | |
| </div> | |
| <div class="nav-item" data-section="dhcp"> | |
| <i class="fas fa-server"></i> | |
| <span>DHCP</span> | |
| </div> | |
| <div class="nav-item" data-section="nat"> | |
| <i class="fas fa-exchange-alt"></i> | |
| <span>NAT</span> | |
| </div> | |
| <div class="nav-item" data-section="firewall"> | |
| <i class="fas fa-shield-alt"></i> | |
| <span>Firewall</span> | |
| </div> | |
| <div class="nav-item" data-section="router"> | |
| <i class="fas fa-route"></i> | |
| <span>Router</span> | |
| </div> | |
| <div class="nav-item" data-section="bridge"> | |
| <i class="fas fa-link"></i> | |
| <span>Bridge</span> | |
| </div> | |
| <div class="nav-item" data-section="sessions"> | |
| <i class="fas fa-users"></i> | |
| <span>Sessions</span> | |
| </div> | |
| <div class="nav-item" data-section="logs"> | |
| <i class="fas fa-file-alt"></i> | |
| <span>Logs</span> | |
| </div> | |
| <div class="nav-item" data-section="vpn"> | |
| <i class="fas fa-shield-alt"></i> | |
| <span>VPN</span> | |
| </div> | |
| <div class="nav-item" data-section="config"> | |
| <i class="fas fa-cog"></i> | |
| <span>Config</span> | |
| </div> | |
| </div> | |
| </nav> | |
| <!-- Main Content --> | |
| <main class="main-content"> | |
| <!-- Dashboard Section --> | |
| <section id="dashboard" class="content-section active"> | |
| <div class="section-header"> | |
| <h2>System Dashboard</h2> | |
| <p>Overview of Virtual ISP Stack components and performance</p> | |
| </div> | |
| <!-- System Status Cards --> | |
| <div class="stats-grid"> | |
| <div class="stat-card"> | |
| <div class="stat-icon"> | |
| <i class="fas fa-server"></i> | |
| </div> | |
| <div class="stat-content"> | |
| <h3 id="dhcpLeaseCount">0</h3> | |
| <p>DHCP Leases</p> | |
| </div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-icon"> | |
| <i class="fas fa-exchange-alt"></i> | |
| </div> | |
| <div class="stat-content"> | |
| <h3 id="natSessionCount">0</h3> | |
| <p>NAT Sessions</p> | |
| </div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-icon"> | |
| <i class="fas fa-shield-alt"></i> | |
| </div> | |
| <div class="stat-content"> | |
| <h3 id="firewallRuleCount">0</h3> | |
| <p>Firewall Rules</p> | |
| </div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-icon"> | |
| <i class="fas fa-link"></i> | |
| </div> | |
| <div class="stat-content"> | |
| <h3 id="bridgeClientCount">0</h3> | |
| <p>Bridge Clients</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Component Status --> | |
| <div class="component-status"> | |
| <h3>Component Status</h3> | |
| <div class="component-grid" id="componentStatus"> | |
| <!-- Component status items will be populated by JavaScript --> | |
| </div> | |
| </div> | |
| <!-- Real-time Charts --> | |
| <div class="charts-container"> | |
| <div class="chart-card"> | |
| <h3>Network Traffic</h3> | |
| <canvas id="trafficChart"></canvas> | |
| </div> | |
| <div class="chart-card"> | |
| <h3>Connection Distribution</h3> | |
| <canvas id="connectionChart"></canvas> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- DHCP Section --> | |
| <section id="dhcp" class="content-section"> | |
| <div class="section-header"> | |
| <h2>DHCP Management</h2> | |
| <p>Manage DHCP leases and configuration</p> | |
| </div> | |
| <div class="table-container"> | |
| <div class="table-header"> | |
| <h3>Active Leases</h3> | |
| <button class="btn btn-secondary" onclick="refreshDHCPLeases()"> | |
| <i class="fas fa-sync-alt"></i> Refresh | |
| </button> | |
| </div> | |
| <div class="table-wrapper"> | |
| <table id="dhcpTable"> | |
| <thead> | |
| <tr> | |
| <th>MAC Address</th> | |
| <th>IP Address</th> | |
| <th>Lease Time</th> | |
| <th>Remaining</th> | |
| <th>State</th> | |
| <th>Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody id="dhcpTableBody"> | |
| <!-- DHCP leases will be populated here --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- NAT Section --> | |
| <section id="nat" class="content-section"> | |
| <div class="section-header"> | |
| <h2>NAT Management</h2> | |
| <p>Network Address Translation sessions and statistics</p> | |
| </div> | |
| <div class="nat-stats"> | |
| <div class="stat-row"> | |
| <div class="stat-item"> | |
| <span class="stat-label">Active Sessions:</span> | |
| <span class="stat-value" id="natActiveSessions">0</span> | |
| </div> | |
| <div class="stat-item"> | |
| <span class="stat-label">Port Utilization:</span> | |
| <span class="stat-value" id="natPortUtilization">0%</span> | |
| </div> | |
| <div class="stat-item"> | |
| <span class="stat-label">Bytes Translated:</span> | |
| <span class="stat-value" id="natBytesTranslated">0</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="table-container"> | |
| <div class="table-header"> | |
| <h3>NAT Sessions</h3> | |
| <button class="btn btn-secondary" onclick="refreshNATSessions()"> | |
| <i class="fas fa-sync-alt"></i> Refresh | |
| </button> | |
| </div> | |
| <div class="table-wrapper"> | |
| <table id="natTable"> | |
| <thead> | |
| <tr> | |
| <th>Virtual IP:Port</th> | |
| <th>Real IP:Port</th> | |
| <th>Host IP:Port</th> | |
| <th>Protocol</th> | |
| <th>Duration</th> | |
| <th>Bytes In/Out</th> | |
| <th>Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody id="natTableBody"> | |
| <!-- NAT sessions will be populated here --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Firewall Section --> | |
| <section id="firewall" class="content-section"> | |
| <div class="section-header"> | |
| <h2>Firewall Management</h2> | |
| <p>Configure firewall rules and monitor traffic</p> | |
| </div> | |
| <div class="firewall-controls"> | |
| <button class="btn btn-primary" onclick="showAddRuleModal()"> | |
| <i class="fas fa-plus"></i> Add Rule | |
| </button> | |
| <button class="btn btn-secondary" onclick="refreshFirewallRules()"> | |
| <i class="fas fa-sync-alt"></i> Refresh | |
| </button> | |
| </div> | |
| <div class="table-container"> | |
| <div class="table-header"> | |
| <h3>Firewall Rules</h3> | |
| </div> | |
| <div class="table-wrapper"> | |
| <table id="firewallTable"> | |
| <thead> | |
| <tr> | |
| <th>Priority</th> | |
| <th>Rule ID</th> | |
| <th>Action</th> | |
| <th>Direction</th> | |
| <th>Source</th> | |
| <th>Destination</th> | |
| <th>Protocol</th> | |
| <th>Hits</th> | |
| <th>Status</th> | |
| <th>Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody id="firewallTableBody"> | |
| <!-- Firewall rules will be populated here --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Router Section --> | |
| <section id="router" class="content-section"> | |
| <div class="section-header"> | |
| <h2>Router Management</h2> | |
| <p>Routing table and network interfaces</p> | |
| </div> | |
| <div class="router-tabs"> | |
| <div class="tab-buttons"> | |
| <button class="tab-btn active" data-tab="routes">Routes</button> | |
| <button class="tab-btn" data-tab="interfaces">Interfaces</button> | |
| <button class="tab-btn" data-tab="arp">ARP Table</button> | |
| </div> | |
| <div class="tab-content"> | |
| <div id="routes" class="tab-pane active"> | |
| <div class="table-container"> | |
| <table id="routesTable"> | |
| <thead> | |
| <tr> | |
| <th>Destination</th> | |
| <th>Gateway</th> | |
| <th>Interface</th> | |
| <th>Metric</th> | |
| <th>Type</th> | |
| <th>Use Count</th> | |
| <th>Last Used</th> | |
| </tr> | |
| </thead> | |
| <tbody id="routesTableBody"> | |
| <!-- Routes will be populated here --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <div id="interfaces" class="tab-pane"> | |
| <div class="table-container"> | |
| <table id="interfacesTable"> | |
| <thead> | |
| <tr> | |
| <th>Name</th> | |
| <th>IP Address</th> | |
| <th>Network</th> | |
| <th>MTU</th> | |
| <th>Status</th> | |
| <th>Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody id="interfacesTableBody"> | |
| <!-- Interfaces will be populated here --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <div id="arp" class="tab-pane"> | |
| <div class="table-container"> | |
| <table id="arpTable"> | |
| <thead> | |
| <tr> | |
| <th>IP Address</th> | |
| <th>MAC Address</th> | |
| <th>Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody id="arpTableBody"> | |
| <!-- ARP entries will be populated here --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Bridge Section --> | |
| <section id="bridge" class="content-section"> | |
| <div class="section-header"> | |
| <h2>Packet Bridge</h2> | |
| <p>Connected clients and bridge statistics</p> | |
| </div> | |
| <div class="bridge-info"> | |
| <div class="info-card"> | |
| <h4>WebSocket Server</h4> | |
| <p>Port: 8765</p> | |
| <p>Status: <span class="status-active">Active</span></p> | |
| </div> | |
| <div class="info-card"> | |
| <h4>TCP Server</h4> | |
| <p>Port: 8766</p> | |
| <p>Status: <span class="status-active">Active</span></p> | |
| </div> | |
| </div> | |
| <div class="table-container"> | |
| <div class="table-header"> | |
| <h3>Connected Clients</h3> | |
| <button class="btn btn-secondary" onclick="refreshBridgeClients()"> | |
| <i class="fas fa-sync-alt"></i> Refresh | |
| </button> | |
| </div> | |
| <div class="table-wrapper"> | |
| <table id="bridgeTable"> | |
| <thead> | |
| <tr> | |
| <th>Client ID</th> | |
| <th>Type</th> | |
| <th>Remote Address</th> | |
| <th>Connected Time</th> | |
| <th>Packets In/Out</th> | |
| <th>Bytes In/Out</th> | |
| <th>Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody id="bridgeTableBody"> | |
| <!-- Bridge clients will be populated here --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Sessions Section --> | |
| <section id="sessions" class="content-section"> | |
| <div class="section-header"> | |
| <h2>Session Tracking</h2> | |
| <p>Unified view of all network sessions</p> | |
| </div> | |
| <div class="session-summary" id="sessionSummary"> | |
| <!-- Session summary will be populated here --> | |
| </div> | |
| <div class="table-container"> | |
| <div class="table-header"> | |
| <h3>Active Sessions</h3> | |
| <div class="table-controls"> | |
| <select id="sessionTypeFilter" onchange="filterSessions()"> | |
| <option value="">All Types</option> | |
| <option value="DHCP_LEASE">DHCP Lease</option> | |
| <option value="NAT_SESSION">NAT Session</option> | |
| <option value="TCP_CONNECTION">TCP Connection</option> | |
| <option value="SOCKET_CONNECTION">Socket Connection</option> | |
| <option value="BRIDGE_CLIENT">Bridge Client</option> | |
| </select> | |
| <button class="btn btn-secondary" onclick="refreshSessions()"> | |
| <i class="fas fa-sync-alt"></i> Refresh | |
| </button> | |
| </div> | |
| </div> | |
| <div class="table-wrapper"> | |
| <table id="sessionsTable"> | |
| <thead> | |
| <tr> | |
| <th>Session ID</th> | |
| <th>Type</th> | |
| <th>State</th> | |
| <th>Virtual IP:Port</th> | |
| <th>Real IP:Port</th> | |
| <th>Protocol</th> | |
| <th>Duration</th> | |
| <th>Idle Time</th> | |
| <th>Metrics</th> | |
| </tr> | |
| </thead> | |
| <tbody id="sessionsTableBody"> | |
| <!-- Sessions will be populated here --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Logs Section --> | |
| <section id="logs" class="content-section"> | |
| <div class="section-header"> | |
| <h2>System Logs</h2> | |
| <p>Monitor system events and troubleshoot issues</p> | |
| </div> | |
| <div class="log-controls"> | |
| <div class="log-filters"> | |
| <select id="logLevelFilter" onchange="filterLogs()"> | |
| <option value="">All Levels</option> | |
| <option value="DEBUG">Debug</option> | |
| <option value="INFO">Info</option> | |
| <option value="WARNING">Warning</option> | |
| <option value="ERROR">Error</option> | |
| <option value="CRITICAL">Critical</option> | |
| </select> | |
| <select id="logCategoryFilter" onchange="filterLogs()"> | |
| <option value="">All Categories</option> | |
| <option value="SYSTEM">System</option> | |
| <option value="DHCP">DHCP</option> | |
| <option value="NAT">NAT</option> | |
| <option value="FIREWALL">Firewall</option> | |
| <option value="TCP">TCP</option> | |
| <option value="ROUTER">Router</option> | |
| <option value="BRIDGE">Bridge</option> | |
| <option value="SOCKET">Socket</option> | |
| <option value="SESSION">Session</option> | |
| <option value="SECURITY">Security</option> | |
| </select> | |
| <input type="text" id="logSearchInput" placeholder="Search logs..." onkeyup="searchLogs()"> | |
| </div> | |
| <div class="log-actions"> | |
| <button class="btn btn-secondary" onclick="refreshLogs()"> | |
| <i class="fas fa-sync-alt"></i> Refresh | |
| </button> | |
| <button class="btn btn-danger" onclick="clearLogs()"> | |
| <i class="fas fa-trash"></i> Clear | |
| </button> | |
| </div> | |
| </div> | |
| <div class="log-container" id="logContainer"> | |
| <!-- Log entries will be populated here --> | |
| </div> | |
| </section> | |
| <!-- VPN Section --> | |
| <section id="vpn" class="content-section"> | |
| <div class="section-header"> | |
| <h2>VPN Management</h2> | |
| <p>OpenVPN server management and client connections</p> | |
| </div> | |
| <!-- VPN Server Status --> | |
| <div class="vpn-status"> | |
| <div class="status-card"> | |
| <div class="status-header"> | |
| <h3>OpenVPN Server</h3> | |
| <div class="server-controls"> | |
| <button class="btn btn-success" id="startVpnBtn" onclick="startVpnServer()"> | |
| <i class="fas fa-play"></i> Start | |
| </button> | |
| <button class="btn btn-danger" id="stopVpnBtn" onclick="stopVpnServer()"> | |
| <i class="fas fa-stop"></i> Stop | |
| </button> | |
| <button class="btn btn-secondary" onclick="refreshVpnStatus()"> | |
| <i class="fas fa-sync-alt"></i> Refresh | |
| </button> | |
| </div> | |
| </div> | |
| <div class="status-info"> | |
| <div class="info-row"> | |
| <span class="info-label">Status:</span> | |
| <span class="info-value" id="vpnServerStatus">Unknown</span> | |
| </div> | |
| <div class="info-row"> | |
| <span class="info-label">Server IP:</span> | |
| <span class="info-value" id="vpnServerIp">-</span> | |
| </div> | |
| <div class="info-row"> | |
| <span class="info-label">Port:</span> | |
| <span class="info-value" id="vpnServerPort">-</span> | |
| </div> | |
| <div class="info-row"> | |
| <span class="info-label">Connected Clients:</span> | |
| <span class="info-value" id="vpnConnectedClients">0</span> | |
| </div> | |
| <div class="info-row"> | |
| <span class="info-label">Uptime:</span> | |
| <span class="info-value" id="vpnUptime">-</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- VPN Statistics --> | |
| <div class="vpn-stats"> | |
| <div class="stat-item"> | |
| <span class="stat-label">Total Bytes Received:</span> | |
| <span class="stat-value" id="vpnBytesReceived">0</span> | |
| </div> | |
| <div class="stat-item"> | |
| <span class="stat-label">Total Bytes Sent:</span> | |
| <span class="stat-value" id="vpnBytesSent">0</span> | |
| </div> | |
| </div> | |
| <!-- Connected Clients --> | |
| <div class="table-container"> | |
| <div class="table-header"> | |
| <h3>Connected VPN Clients</h3> | |
| <div class="table-controls"> | |
| <button class="btn btn-primary" onclick="showGenerateConfigModal()"> | |
| <i class="fas fa-plus"></i> Generate Client Config | |
| </button> | |
| <button class="btn btn-secondary" onclick="refreshVpnClients()"> | |
| <i class="fas fa-sync-alt"></i> Refresh | |
| </button> | |
| </div> | |
| </div> | |
| <div class="table-wrapper"> | |
| <table id="vpnClientsTable"> | |
| <thead> | |
| <tr> | |
| <th>Client ID</th> | |
| <th>Common Name</th> | |
| <th>VPN IP Address</th> | |
| <th>Connected Since</th> | |
| <th>Bytes Received</th> | |
| <th>Bytes Sent</th> | |
| <th>Status</th> | |
| <th>Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody id="vpnClientsTableBody"> | |
| <!-- VPN clients will be populated here --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Config Section --> | |
| <section id="config" class="content-section"> | |
| <div class="section-header"> | |
| <h2>System Configuration</h2> | |
| <p>Configure system parameters and settings</p> | |
| </div> | |
| <div class="config-container"> | |
| <div class="config-section"> | |
| <h3>DHCP Configuration</h3> | |
| <div class="config-form" id="dhcpConfig"> | |
| <!-- DHCP config form will be populated here --> | |
| </div> | |
| </div> | |
| <div class="config-section"> | |
| <h3>NAT Configuration</h3> | |
| <div class="config-form" id="natConfig"> | |
| <!-- NAT config form will be populated here --> | |
| </div> | |
| </div> | |
| <div class="config-section"> | |
| <h3>Firewall Configuration</h3> | |
| <div class="config-form" id="firewallConfig"> | |
| <!-- Firewall config form will be populated here --> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="config-actions"> | |
| <button class="btn btn-primary" onclick="saveConfiguration()"> | |
| <i class="fas fa-save"></i> Save Configuration | |
| </button> | |
| <button class="btn btn-secondary" onclick="resetConfiguration()"> | |
| <i class="fas fa-undo"></i> Reset to Defaults | |
| </button> | |
| </div> | |
| </section> | |
| </main> | |
| </div> | |
| <!-- Modals --> | |
| <div id="addRuleModal" class="modal"> | |
| <div class="modal-content"> | |
| <div class="modal-header"> | |
| <h3>Add Firewall Rule</h3> | |
| <span class="close" onclick="closeModal('addRuleModal')">×</span> | |
| </div> | |
| <div class="modal-body"> | |
| <form id="addRuleForm"> | |
| <div class="form-group"> | |
| <label for="ruleId">Rule ID:</label> | |
| <input type="text" id="ruleId" name="ruleId" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="rulePriority">Priority:</label> | |
| <input type="number" id="rulePriority" name="priority" value="100" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="ruleAction">Action:</label> | |
| <select id="ruleAction" name="action" required> | |
| <option value="ACCEPT">Accept</option> | |
| <option value="DROP">Drop</option> | |
| <option value="REJECT">Reject</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="ruleDirection">Direction:</label> | |
| <select id="ruleDirection" name="direction"> | |
| <option value="BOTH">Both</option> | |
| <option value="INBOUND">Inbound</option> | |
| <option value="OUTBOUND">Outbound</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="ruleSourceIp">Source IP:</label> | |
| <input type="text" id="ruleSourceIp" name="source_ip" placeholder="e.g., 192.168.1.0/24"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="ruleDestIp">Destination IP:</label> | |
| <input type="text" id="ruleDestIp" name="dest_ip" placeholder="e.g., 10.0.0.0/8"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="ruleSourcePort">Source Port:</label> | |
| <input type="text" id="ruleSourcePort" name="source_port" placeholder="e.g., 80, 80-90, 80,443"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="ruleDestPort">Destination Port:</label> | |
| <input type="text" id="ruleDestPort" name="dest_port" placeholder="e.g., 80, 80-90, 80,443"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="ruleProtocol">Protocol:</label> | |
| <select id="ruleProtocol" name="protocol"> | |
| <option value="">Any</option> | |
| <option value="TCP">TCP</option> | |
| <option value="UDP">UDP</option> | |
| <option value="ICMP">ICMP</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="ruleDescription">Description:</label> | |
| <input type="text" id="ruleDescription" name="description" placeholder="Rule description"> | |
| </div> | |
| </form> | |
| </div> | |
| <div class="modal-footer"> | |
| <button type="button" class="btn btn-secondary" onclick="closeModal('addRuleModal')">Cancel</button> | |
| <button type="button" class="btn btn-primary" onclick="addFirewallRule()">Add Rule</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Generate VPN Config Modal --> | |
| <div id="generateConfigModal" class="modal"> | |
| <div class="modal-content"> | |
| <div class="modal-header"> | |
| <h3>Generate VPN Client Configuration</h3> | |
| <span class="close" onclick="closeModal('generateConfigModal')">×</span> | |
| </div> | |
| <div class="modal-body"> | |
| <form id="generateConfigForm"> | |
| <div class="form-group"> | |
| <label for="clientName">Client Name:</label> | |
| <input type="text" id="clientName" name="clientName" required | |
| placeholder="Enter client name (e.g., client1)"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="serverIp">Server IP:</label> | |
| <input type="text" id="serverIp" name="serverIp" required | |
| placeholder="Enter server IP address"> | |
| </div> | |
| </form> | |
| </div> | |
| <div class="modal-footer"> | |
| <button type="button" class="btn btn-secondary" onclick="closeModal('generateConfigModal')">Cancel</button> | |
| <button type="button" class="btn btn-primary" onclick="generateClientConfig()">Generate Config</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Loading Overlay --> | |
| <div id="loadingOverlay" class="loading-overlay"> | |
| <div class="loading-spinner"> | |
| <i class="fas fa-spinner fa-spin"></i> | |
| <p>Loading...</p> | |
| </div> | |
| </div> | |
| <!-- Toast Notifications --> | |
| <div id="toastContainer" class="toast-container"></div> | |
| <!-- JavaScript --> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <script src="app.js"></script> | |
| </body> | |
| </html> | |