peerpigeon
Version:
WebRTC-based peer-to-peer mesh networking library with intelligent routing and signaling server
604 lines (559 loc) โข 33.8 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PeerPigeon - Complete API Testing Suite</title>
<link rel="stylesheet" href="styles.css">
<link rel="icon" type="image/png" sizes="32x32" href="../browser/assets/images/favicon.png">
</head>
<body>
<div class="container">
<header>
<h1>๐ฆ PeerPigeon - Complete API Testing Suite</h1>
<p>Comprehensive browser-based tool for testing all PeerPigeon features</p>
<div class="peer-info">
<span><strong>Peer ID:</strong> <code id="peer-id">Initializing...</code></span>
<span><strong>Status:</strong> <span id="status" class="status disconnected">Disconnected</span></span>
</div>
</header>
<nav class="feature-tabs">
<button class="tab-btn active" data-tab="connection">Connection</button>
<button class="tab-btn" data-tab="messaging">Messaging</button>
<button class="tab-btn" data-tab="media">Media</button>
<button class="tab-btn" data-tab="dht">WebDHT & Storage</button>
<button class="tab-btn" data-tab="crypto">Encryption</button>
<button class="tab-btn" data-tab="network">Network Info</button>
<button class="tab-btn" data-tab="testing">API Testing</button>
</nav>
<!-- Global Message History - Visible across all tabs -->
<section class="global-message-history">
<div class="card">
<h3>๐ฌ Message History</h3>
<div class="message-filters">
<select id="message-filter">
<option value="all">All Messages</option>
<option value="sent">Sent Messages</option>
<option value="received">Received Messages</option>
<option value="broadcast">Broadcast Messages</option>
<option value="direct">Direct Messages</option>
<option value="group">Group Messages</option>
</select>
<button id="export-messages-btn" class="btn tertiary">๐ฅ Export History</button>
</div>
<div id="message-history" class="message-history">
<p class="empty-state">No messages yet</p>
</div>
<div class="button-group">
<button id="clear-messages-btn" class="btn tertiary">Clear History</button>
<button id="refresh-messages-btn" class="btn tertiary">Refresh</button>
</div>
</div>
</section>
<main>
<!-- Connection Management Tab -->
<section id="connection-tab" class="tab-content active">
<h2>๐ Connection Management</h2>
<div class="card">
<h3>๐ Network & Signaling Server</h3>
<div class="input-group">
<label for="network-name">Network Name:</label>
<input type="text" id="network-name" value="global" placeholder="global (e.g., 'gaming', 'work', 'family')">
<small>Create isolated networks or use 'global' for default mesh</small>
</div>
<div class="input-group">
<label for="signaling-url">Server URL:</label>
<input type="text" id="signaling-url" value="ws://localhost:3000" placeholder="ws://localhost:3000">
</div>
<div class="checkbox-group">
<label><input type="checkbox" id="allow-global-fallback" checked> Allow Global Fallback</label>
<small>Automatically fallback to global network when current network is empty</small>
</div>
<div class="button-group">
<button id="connect-btn" class="btn primary">Connect</button>
<button id="disconnect-btn" class="btn secondary" disabled>Disconnect</button>
<button id="cleanup-btn" class="btn tertiary">Cleanup Stale Data</button>
</div>
<div id="network-status" class="network-status" style="display: none;">
<div class="status-info">
<span><strong>Current Network:</strong> <span id="current-network">global</span></span>
<span id="fallback-indicator" style="display: none;" class="fallback-badge">Fallback Mode</span>
</div>
<div class="status-info" id="original-network-info" style="display: none;">
<span><strong>Original Network:</strong> <span id="original-network"></span></span>
</div>
</div>
</div>
<div class="card">
<h3>๐ฏ Quick Network Switch</h3>
<p class="description">Quickly switch between common networks (only when disconnected)</p>
<div class="quick-networks">
<button class="btn tertiary network-btn" data-network="global">Global</button>
<button class="btn tertiary network-btn" data-network="gaming">Gaming</button>
<button class="btn tertiary network-btn" data-network="work">Work</button>
<button class="btn tertiary network-btn" data-network="family">Family</button>
<button class="btn tertiary network-btn" data-network="test">Test</button>
</div>
</div>
<div class="card">
<h3>Mesh Configuration</h3>
<div class="config-grid">
<div class="input-group">
<label for="max-peers">Max Peers:</label>
<input type="number" id="max-peers" value="3" min="1" max="10">
</div>
<div class="input-group">
<label for="min-peers">Min Peers:</label>
<input type="number" id="min-peers" value="2" min="1" max="10">
</div>
<div class="checkbox-group">
<label><input type="checkbox" id="auto-connect" checked> Auto Connect</label>
<label><input type="checkbox" id="auto-discovery" checked> Auto Discovery</label>
<label><input type="checkbox" id="eviction-strategy" checked> Eviction Strategy</label>
<label><input type="checkbox" id="xor-routing" checked> XOR Routing</label>
</div>
<div class="checkbox-group">
<label><input type="checkbox" id="enable-crypto" checked> Enable Crypto</label>
<label><input type="checkbox" id="enable-webdht" checked> Enable WebDHT</label>
<label><input type="checkbox" id="enable-distributed-storage" checked> Enable Distributed Storage</label>
</div>
<div class="checkbox-group">
<label><input type="checkbox" id="enable-gossip"> Disable Gossip Protocol</label>
<label><input type="checkbox" id="enable-hole-punching"> Disable Hole Punching</label>
<label><input type="checkbox" id="enable-backup-connections"> Disable Backup Connections</label>
</div>
</div>
<button id="apply-config-btn" class="btn tertiary">Apply Configuration</button>
<button id="get-current-config-btn" class="btn tertiary">Show Current Config</button>
</div>
<div class="card">
<h3>Connected Peers (<span id="peer-count">0</span>)</h3>
<div id="peers-list" class="peers-list">
<p class="empty-state">No peers connected</p>
</div>
<div class="peer-actions">
<input type="text" id="manual-peer-id" placeholder="Enter peer ID to connect">
<button id="connect-peer-btn" class="btn tertiary">Connect to Peer</button>
<button id="force-connect-all-btn" class="btn tertiary">Force Connect All</button>
</div>
</div>
</section>
<!-- Messaging Tab -->
<section id="messaging-tab" class="tab-content">
<h2>๐ฌ Messaging & Communication</h2>
<div class="card">
<h3>๐ข Broadcast Messages</h3>
<p class="description">Send messages to all connected peers in the mesh</p>
<div class="message-input">
<div class="input-group">
<label for="broadcast-message">Broadcast Message:</label>
<textarea id="broadcast-message" placeholder="Enter your broadcast message..." rows="3"></textarea>
</div>
<div class="message-options">
<label><input type="checkbox" id="encrypt-broadcast" checked> Encrypt Message</label>
<label><input type="checkbox" id="high-priority-broadcast"> High Priority</label>
</div>
<div class="button-group">
<button id="send-broadcast-btn" class="btn primary">๐ข Send Broadcast</button>
<button id="send-anonymous-btn" class="btn secondary">๐ญ Send Anonymous</button>
</div>
</div>
</div>
<div class="card">
<h3>๐ง Direct Messages</h3>
<p class="description">Send private messages to specific peers</p>
<div class="message-input">
<div class="input-group">
<label for="target-peer">Target Peer:</label>
<select id="target-peer-select">
<option value="">Select a connected peer...</option>
</select>
<input type="text" id="target-peer" placeholder="Or enter peer ID manually">
</div>
<div class="input-group">
<label for="direct-message">Private Message:</label>
<textarea id="direct-message" placeholder="Enter your direct message..." rows="3"></textarea>
</div>
<div class="message-options">
<label><input type="checkbox" id="encrypt-direct" checked> Encrypt Message</label>
<label><input type="checkbox" id="request-receipt"> Request Read Receipt</label>
<label><input type="checkbox" id="ephemeral-message"> Ephemeral (Auto-delete)</label>
</div>
<div class="button-group">
<button id="send-direct-btn" class="btn primary">๐ง Send Direct Message</button>
<button id="send-file-btn" class="btn secondary">๐ Send File</button>
</div>
</div>
</div>
<div class="card">
<h3>๐ฌ Group Chat</h3>
<p class="description">Create and manage group conversations</p>
<div class="group-controls">
<div class="input-group">
<label for="group-name">Group Name:</label>
<input type="text" id="group-name" placeholder="Enter group name">
</div>
<div class="input-group">
<label for="group-members">Members (comma-separated peer IDs):</label>
<textarea id="group-members" placeholder="peer1,peer2,peer3..." rows="2"></textarea>
</div>
<div class="button-group">
<button id="create-group-btn" class="btn primary">Create Group</button>
<button id="join-group-btn" class="btn secondary">Join Group</button>
<button id="leave-group-btn" class="btn tertiary">Leave Group</button>
</div>
<div class="input-group">
<label for="group-message">Group Message:</label>
<textarea id="group-message" placeholder="Enter group message..." rows="3"></textarea>
<button id="send-group-btn" class="btn primary">Send to Group</button>
</div>
</div>
</div>
</section>
<!-- Media Tab -->
<section id="media-tab" class="tab-content">
<h2>๐ฅ Media Management</h2>
<div class="card">
<h3>Local Media</h3>
<div class="media-controls">
<div class="checkbox-group">
<label><input type="checkbox" id="enable-video"> Enable Video</label>
<label><input type="checkbox" id="enable-audio"> Enable Audio</label>
</div>
<div class="device-selectors">
<div class="input-group">
<label for="camera-select">Camera:</label>
<select id="camera-select">
<option value="">Select camera...</option>
</select>
</div>
<div class="input-group">
<label for="microphone-select">Microphone:</label>
<select id="microphone-select">
<option value="">Select microphone...</option>
</select>
</div>
</div>
<div class="button-group">
<button id="start-media-btn" class="btn primary">Start Media</button>
<button id="stop-media-btn" class="btn secondary" disabled>Stop Media</button>
<button id="enumerate-devices-btn" class="btn tertiary">Refresh Devices</button>
</div>
<div class="media-toggle">
<button id="toggle-video-btn" class="btn tertiary" disabled>Toggle Video</button>
<button id="toggle-audio-btn" class="btn tertiary" disabled>Toggle Audio</button>
</div>
</div>
<div class="media-preview">
<video id="local-video" autoplay muted playsinline style="display: none;"></video>
<div id="media-status" class="media-status">No active media stream</div>
</div>
</div>
<div class="card">
<h3>Remote Streams</h3>
<div id="remote-streams" class="remote-streams">
<p class="empty-state">No remote streams</p>
</div>
</div>
<div class="card">
<h3>Media Information</h3>
<div id="media-info" class="info-display">
<div>Local Stream: <span id="local-stream-status">None</span></div>
<div>Video Enabled: <span id="video-enabled-status">No</span></div>
<div>Audio Enabled: <span id="audio-enabled-status">No</span></div>
<div>Active Camera: <span id="active-camera">None</span></div>
<div>Active Microphone: <span id="active-microphone">None</span></div>
</div>
</div>
</section>
<!-- WebDHT & Storage Tab -->
<section id="dht-tab" class="tab-content">
<h2>๐๏ธ WebDHT (Low-Level DHT)</h2>
<p class="description">Low-level distributed hash table for raw key-value storage without encryption or access control</p>
<div class="card">
<h3>Store Raw Data</h3>
<div class="dht-form">
<div class="input-group">
<label for="dht-key">Key:</label>
<input type="text" id="dht-key" placeholder="Enter raw storage key">
</div>
<div class="input-group">
<label for="dht-value">Value (JSON):</label>
<textarea id="dht-value" placeholder='{"example": "data"}' rows="3"></textarea>
</div>
<div class="input-group">
<label for="dht-ttl">TTL (ms, optional):</label>
<input type="number" id="dht-ttl" placeholder="Time to live in milliseconds">
</div>
<div class="button-group">
<button id="dht-put-btn" class="btn primary">Store Data</button>
<button id="dht-update-btn" class="btn secondary">Update Data</button>
</div>
</div>
</div>
<div class="card">
<h3>Retrieve Data</h3>
<div class="input-group">
<label for="dht-get-key">Key:</label>
<input type="text" id="dht-get-key" placeholder="Enter key to retrieve">
</div>
<div class="button-group">
<button id="dht-get-btn" class="btn primary">Get Data</button>
<button id="dht-delete-btn" class="btn danger">Delete Key</button>
</div>
<div id="dht-result" class="result-display"></div>
</div>
<div class="card">
<h3>Subscriptions</h3>
<div class="input-group">
<label for="dht-subscribe-key">Key to Subscribe:</label>
<input type="text" id="dht-subscribe-key" placeholder="Enter key to monitor">
</div>
<div class="button-group">
<button id="dht-subscribe-btn" class="btn primary">Subscribe</button>
<button id="dht-unsubscribe-btn" class="btn secondary">Unsubscribe</button>
</div>
<div id="dht-subscriptions" class="subscriptions-list">
<p class="empty-state">No active subscriptions</p>
</div>
</div>
<div class="card">
<h3>DHT Activity Log</h3>
<div id="dht-log" class="activity-log">
<p class="empty-state">No DHT activity yet</p>
</div>
<button id="clear-dht-log-btn" class="btn tertiary">Clear Log</button>
</div>
<div class="card">
<h3>๐๏ธ Distributed Storage (High-Level)</h3>
<p class="description">High-level storage with encryption, access control, and storage spaces - built on top of WebDHT</p>
<div class="storage-controls">
<div class="button-group">
<button id="storage-enable-btn" class="btn primary">Enable Storage</button>
<button id="storage-disable-btn" class="btn secondary">Disable Storage</button>
<button id="storage-status-btn" class="btn tertiary">Get Status</button>
</div>
<div class="storage-form">
<div class="input-group">
<label for="storage-key">Storage Key:</label>
<input type="text" id="storage-key" placeholder="Enter unique storage key">
</div>
<div class="input-group">
<label for="storage-data">Data (JSON):</label>
<textarea id="storage-data" placeholder='{"persistent": "data"}' rows="3"></textarea>
</div>
<div class="input-group">
<label for="storage-space">Storage Space:</label>
<select id="storage-space">
<option value="private">Private (encrypted, owner-only)</option>
<option value="public">Public (unencrypted, readable by all)</option>
<option value="frozen">Frozen (immutable, readable by all)</option>
</select>
</div>
<div class="button-group">
<button id="storage-put-btn" class="btn primary">Store Data</button>
<button id="storage-get-btn" class="btn secondary">Retrieve Data</button>
<button id="storage-delete-btn" class="btn danger">Delete Data</button>
</div>
</div>
<div class="storage-management">
<div class="button-group">
<button id="storage-list-btn" class="btn tertiary">List All Keys</button>
<button id="storage-stats-btn" class="btn tertiary">Storage Stats</button>
<button id="storage-clear-btn" class="btn danger">Clear All Storage</button>
</div>
</div>
</div>
<div id="storage-result" class="result-display"></div>
<div class="card">
<h4>Storage Activity Log</h4>
<div id="storage-log" class="activity-log">
<p class="empty-state">No storage activity yet</p>
</div>
<button id="clear-storage-log-btn" class="btn tertiary">Clear Storage Log</button>
</div>
</div>
</section>
<!-- Encryption Tab -->
<section id="crypto-tab" class="tab-content">
<h2>๐ Encryption & Security</h2>
<div class="card">
<h3>Crypto Status</h3>
<div id="crypto-status" class="info-display">
<div>Crypto Enabled: <span id="crypto-enabled-status">Unknown</span></div>
<div>Public Key: <span id="public-key-display">Not available</span></div>
<div>Key Exchanges: <span id="key-exchanges-count">0</span></div>
</div>
<div class="button-group">
<button id="refresh-crypto-status-btn" class="btn tertiary">Refresh Status</button>
<button id="force-crypto-init-btn" class="btn secondary">Force Crypto Init</button>
<button id="exchange-keys-btn" class="btn primary">Exchange Keys</button>
</div>
</div>
<div class="card">
<h3>Encrypted Messaging</h3>
<div class="input-group">
<label for="encrypted-message">Message:</label>
<textarea id="encrypted-message" placeholder="Enter message to encrypt and broadcast..." rows="3"></textarea>
</div>
<div class="input-group">
<label for="group-id">Group ID (optional):</label>
<input type="text" id="group-id" placeholder="Enter group encryption key ID">
</div>
<button id="send-encrypted-btn" class="btn primary">Send Encrypted Broadcast</button>
</div>
<div class="card">
<h3>Key Exchange</h3>
<div class="input-group">
<label for="key-exchange-peer">Peer ID:</label>
<input type="text" id="key-exchange-peer" placeholder="Enter peer ID for key exchange">
</div>
<button id="exchange-keys-btn" class="btn primary">Exchange Keys</button>
</div>
<div class="card">
<h3>Manual Key Management</h3>
<div class="input-group">
<label for="manual-peer-id">Peer ID:</label>
<input type="text" id="manual-peer-id" placeholder="Enter peer ID">
</div>
<div class="input-group">
<label for="manual-public-key">Public Key:</label>
<textarea id="manual-public-key" placeholder="Enter peer's public key..." rows="3"></textarea>
</div>
<button id="add-peer-key-btn" class="btn primary">Add Peer Key</button>
</div>
<div class="card">
<h3>Encryption Activity Log</h3>
<div id="crypto-log" class="activity-log">
<p class="empty-state">No encryption activity yet</p>
</div>
<button id="clear-crypto-log-btn" class="btn tertiary">Clear Log</button>
</div>
</section>
<!-- Network Information Tab -->
<section id="network-tab" class="tab-content">
<h2>๐ Network Information & Monitoring</h2>
<div class="card">
<h3>Network Status</h3>
<div id="network-status" class="status-grid">
<div class="status-item">
<label>Connected:</label>
<span id="network-connected">No</span>
</div>
<div class="status-item">
<label>Connected Peers:</label>
<span id="network-peer-count">0</span>
</div>
<div class="status-item">
<label>Discovered Peers:</label>
<span id="network-discovered-count">0</span>
</div>
<div class="status-item">
<label>Signaling URL:</label>
<span id="network-signaling-url">None</span>
</div>
<div class="status-item">
<label>Uptime:</label>
<span id="network-uptime">0s</span>
</div>
<div class="status-item">
<label>Can Accept More:</label>
<span id="network-can-accept">Unknown</span>
</div>
</div>
<button id="refresh-status-btn" class="btn tertiary">Refresh Status</button>
</div>
<div class="card">
<h3>Peer State Summary</h3>
<div id="peer-state-summary" class="peer-states">
<p class="empty-state">No peer state information available</p>
</div>
<button id="get-peer-states-btn" class="btn tertiary">Get Peer States</button>
</div>
<div class="card">
<h3>Connection Monitoring</h3>
<div class="button-group">
<button id="start-monitoring-btn" class="btn primary">Start Monitoring</button>
<button id="stop-monitoring-btn" class="btn secondary" disabled>Stop Monitoring</button>
<button id="debug-connectivity-btn" class="btn tertiary">Debug Connectivity</button>
</div>
<div id="connection-stats" class="stats-display">
<p class="empty-state">No monitoring data available</p>
</div>
</div>
<div class="card">
<h3>Discovered Peers</h3>
<div id="discovered-peers" class="discovered-peers-list">
<p class="empty-state">No peers discovered</p>
</div>
</div>
</section>
<!-- API Testing Tab -->
<section id="testing-tab" class="tab-content">
<h2>๐งช API Testing & Utilities</h2>
<div class="card">
<h3>Utility Functions</h3>
<div class="button-group">
<button id="validate-peer-id-btn" class="btn primary">Validate Peer ID</button>
<button id="force-connect-all-api-btn" class="btn secondary">Force Connect All Peers</button>
<button id="cleanup-stale-btn" class="btn tertiary">Cleanup Stale Data</button>
</div>
<div class="input-group">
<label for="test-peer-id">Peer ID to Validate:</label>
<input type="text" id="test-peer-id" placeholder="Enter 40-character hex peer ID">
</div>
<div id="utility-results" class="result-display"></div>
</div>
<div class="card">
<h3>Performance Testing</h3>
<div class="input-group">
<label for="test-message-count">Number of Messages:</label>
<input type="number" id="test-message-count" value="10" min="1" max="100">
</div>
<div class="input-group">
<label for="test-message-size">Message Size (chars):</label>
<input type="number" id="test-message-size" value="100" min="1" max="10000">
</div>
<div class="button-group">
<button id="performance-test-btn" class="btn primary">Run Performance Test</button>
<button id="stress-test-btn" class="btn danger">Run Stress Test</button>
</div>
<div id="performance-results" class="result-display"></div>
</div>
<div class="card">
<h3>Test Results & Logs</h3>
<div id="test-log" class="test-log">
<p class="empty-state">No test results yet</p>
</div>
<div class="button-group">
<button id="export-logs-btn" class="btn tertiary">Export Logs</button>
<button id="clear-test-log-btn" class="btn tertiary">Clear Test Log</button>
</div>
</div>
<div class="card">
<h3>Error Testing</h3>
<div class="button-group">
<button id="test-invalid-peer-btn" class="btn danger">Test Invalid Peer Connection</button>
<button id="test-malformed-message-btn" class="btn danger">Test Malformed Message</button>
<button id="test-dht-limits-btn" class="btn danger">Test DHT Limits</button>
</div>
<div id="error-test-results" class="result-display"></div>
</div>
</section>
</main>
<footer>
<div class="system-log">
<h3>System Log</h3>
<div id="system-log" class="log-display">
<p class="empty-state">System log will appear here...</p>
</div>
<button id="clear-log-btn" class="btn tertiary">Clear Log</button>
</div>
</footer>
</div>
<!-- Load PeerPigeon library -->
<script src="../../dist/peerpigeon-browser.js"></script>
<!-- Load our application -->
<script src="app.js"></script>
</body>
</html>