| | <!DOCTYPE html> |
| | <html lang="en"> |
| |
|
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>Proxy Rotation Service</title> |
| | <style> |
| | :root { |
| | --bg-color: #0f172a; |
| | --card-bg: #1e293b; |
| | --text-primary: #f1f5f9; |
| | --text-secondary: #94a3b8; |
| | --accent: #3b82f6; |
| | --success: #22c55e; |
| | --error: #ef4444; |
| | } |
| | |
| | body { |
| | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| | background-color: var(--bg-color); |
| | color: var(--text-primary); |
| | margin: 0; |
| | padding: 2rem; |
| | line-height: 1.6; |
| | } |
| | |
| | .container { |
| | max-width: 1200px; |
| | margin: 0 auto; |
| | } |
| | |
| | header { |
| | margin-bottom: 2rem; |
| | border-bottom: 1px solid #334155; |
| | padding-bottom: 1rem; |
| | } |
| | |
| | h1 { |
| | font-weight: 300; |
| | font-size: 2.5rem; |
| | margin: 0; |
| | } |
| | |
| | .status-card { |
| | background-color: var(--card-bg); |
| | border-radius: 12px; |
| | padding: 2rem; |
| | margin-bottom: 2rem; |
| | box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: center; |
| | } |
| | |
| | .status-item { |
| | text-align: center; |
| | } |
| | |
| | .status-label { |
| | color: var(--text-secondary); |
| | font-size: 0.9rem; |
| | margin-bottom: 0.5rem; |
| | } |
| | |
| | .status-value { |
| | font-size: 1.5rem; |
| | font-weight: bold; |
| | } |
| | |
| | .status-value.highlight { |
| | color: var(--accent); |
| | } |
| | |
| | .proxy-list { |
| | background-color: var(--card-bg); |
| | border-radius: 12px; |
| | overflow: hidden; |
| | box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); |
| | } |
| | |
| | table { |
| | width: 100%; |
| | border-collapse: collapse; |
| | } |
| | |
| | th, |
| | td { |
| | padding: 1rem; |
| | text-align: left; |
| | border-bottom: 1px solid #334155; |
| | } |
| | |
| | th { |
| | background-color: #334155; |
| | color: var(--text-primary); |
| | font-weight: 600; |
| | } |
| | |
| | tr:hover { |
| | background-color: #273548; |
| | } |
| | |
| | .latency-good { |
| | color: var(--success); |
| | } |
| | |
| | .latency-bad { |
| | color: var(--error); |
| | } |
| | |
| | .loading { |
| | text-align: center; |
| | padding: 2rem; |
| | color: var(--text-secondary); |
| | } |
| | </style> |
| | </head> |
| |
|
| | <body> |
| | <div class="container"> |
| | <header> |
| | <div style="display: flex; justify-content: space-between; align-items: center;"> |
| | <h1>Proxy Rotation Service</h1> |
| | <div style="font-size: 0.8rem; color: var(--text-secondary);">Auto-refreshing every 2s</div> |
| | </div> |
| | </header> |
| |
|
| | |
| | <div class="status-card"> |
| | <div class="status-item"> |
| | <div class="status-label">Current Best Proxy</div> |
| | <div class="status-value highlight" id="best-proxy-url">Searching...</div> |
| | </div> |
| | <div class="status-item"> |
| | <div class="status-label">Protocol</div> |
| | <div class="status-value" id="best-proxy-protocol">-</div> |
| | </div> |
| | <div class="status-item"> |
| | <div class="status-label">Latency</div> |
| | <div class="status-value" id="best-proxy-latency">-</div> |
| | </div> |
| | <div class="status-item"> |
| | <div class="status-label">Total Active Proxies</div> |
| | <div class="status-value" id="total-proxies">0</div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="proxy-list"> |
| | <table> |
| | <thead> |
| | <tr> |
| | <th>Proxy URL</th> |
| | <th>Protocol</th> |
| | <th>IP</th> |
| | <th>Port</th> |
| | <th>Latency (ms)</th> |
| | </tr> |
| | </thead> |
| | <tbody id="proxy-table-body"> |
| | |
| | </tbody> |
| | </table> |
| | <div id="loading-msg" class="loading">Waiting for results...</div> |
| | </div> |
| | </div> |
| |
|
| | <script> |
| | async def updateDashboard() { |
| | try { |
| | |
| | const statusRes = await fetch('/api/status'); |
| | const statusData = await statusRes.json(); |
| | |
| | const best = statusData.best_proxy; |
| | document.getElementById('total-proxies').innerText = statusData.total_available; |
| | |
| | if (best) { |
| | document.getElementById('best-proxy-url').innerText = best.url; |
| | document.getElementById('best-proxy-protocol').innerText = best.protocol.toUpperCase(); |
| | |
| | const latency = best.latency.toFixed(2); |
| | const latEl = document.getElementById('best-proxy-latency'); |
| | latEl.innerText = latency + ' ms'; |
| | latEl.className = 'status-value ' + (best.latency < 200 ? 'latency-good' : 'latency-bad'); |
| | } else { |
| | document.getElementById('best-proxy-url').innerText = "Searching..."; |
| | document.getElementById('best-proxy-protocol').innerText = "-"; |
| | document.getElementById('best-proxy-latency').innerText = "-"; |
| | } |
| | |
| | |
| | const listRes = await fetch('/api/proxies'); |
| | const listData = await listRes.json(); |
| | |
| | const tbody = document.getElementById('proxy-table-body'); |
| | tbody.innerHTML = ''; |
| | |
| | if (listData.proxies.length > 0) { |
| | document.getElementById('loading-msg').style.display = 'none'; |
| | listData.proxies.forEach(p => { |
| | const tr = document.createElement('tr'); |
| | const latClass = p.latency < 200 ? 'latency-good' : 'latency-bad'; |
| | tr.innerHTML = ` |
| | <td>${p.url}</td> |
| | <td>${p.protocol}</td> |
| | <td>${p.ip}</td> |
| | <td>${p.port}</td> |
| | <td class="${latClass}">${p.latency.toFixed(2)}</td> |
| | `; |
| | tbody.appendChild(tr); |
| | }); |
| | } |
| | |
| | } catch (e) { |
| | console.error("Error updating dashboard:", e); |
| | } |
| | } |
| | |
| | |
| | updateDashboard(); |
| | |
| | |
| | setInterval(updateDashboard, 2000); |
| | </script> |
| | </body> |
| |
|
| | </html> |