diff --git a/assets/pasted-20260225-000344-14072fd8.jpg b/assets/pasted-20260225-000344-14072fd8.jpg new file mode 100644 index 0000000..525a8bd Binary files /dev/null and b/assets/pasted-20260225-000344-14072fd8.jpg differ diff --git a/index.php b/index.php index 953f4e7..fd10b0c 100644 --- a/index.php +++ b/index.php @@ -3,6 +3,7 @@ declare(strict_types=1); @ini_set('display_errors', '1'); @error_reporting(E_ALL); @date_default_timezone_set('UTC'); +$v = time(); // Cache busting version ?> @@ -10,7 +11,7 @@ declare(strict_types=1); 𝐌𝐨𝐧𝐢𝐭𝐨𝐫 𝐔𝐩𝐭𝐢𝐦𝐞 𝐛𝐲 𝐘𝐮𝐦𝐞𝐞 - + @@ -25,7 +26,6 @@ declare(strict_types=1); --dark-sidebar: #1e2329; --text-main: #eaecef; --text-dim: #848e9c; - --neon-glow: 0 0 10px rgba(0, 255, 136, 0.5); --border: #2b3139; } @@ -54,14 +54,9 @@ declare(strict_types=1); z-index: 100; } - .sidebar-header { - padding: 1.5rem; - border-bottom: 1px solid var(--border); - } - + .sidebar-header { padding: 1.5rem; border-bottom: 1px solid var(--border); } .sidebar-header h1 { - font-size: 1.1rem; - margin: 0; + font-size: 1.1rem; margin: 0; background: linear-gradient(90deg, #fff, var(--crypto-blue)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; @@ -86,149 +81,75 @@ declare(strict_types=1); } /* Main Content */ - main { - flex: 1; - display: flex; - flex-direction: column; - overflow-y: auto; - } - + main { flex: 1; display: flex; flex-direction: column; overflow-y: auto; } .top-bar { - height: 60px; - border-bottom: 1px solid var(--border); - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 2rem; - background: var(--dark-bg); - position: sticky; - top: 0; - z-index: 99; + height: 60px; border-bottom: 1px solid var(--border); + display: flex; align-items: center; justify-content: space-between; + padding: 0 2rem; background: var(--dark-bg); + position: sticky; top: 0; z-index: 99; } - .content-area { padding: 2rem; max-width: 1400px; width: 100%; margin: 0 auto; } - /* Cards & Components */ - .dashboard-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: 20px; - margin-bottom: 2rem; - } - - .card { - background: var(--dark-card); - border-radius: 12px; - padding: 1.5rem; - border: 1px solid var(--border); - position: relative; - } - + /* Cards */ + .dashboard-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 2rem; } + .card { background: var(--dark-card); border-radius: 12px; padding: 1.5rem; border: 1px solid var(--border); position: relative; } .stat-val { font-family: 'JetBrains Mono', monospace; font-size: 1.8rem; font-weight: 700; display: block; margin-top: 5px; } .stat-label { color: var(--text-dim); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 1px; } - /* Battery Bars */ - .battery-container { display: flex; gap: 2px; height: 20px; margin-top: 10px; } - .battery-bar { flex: 1; background: #2b3139; border-radius: 2px; position: relative; } - .battery-bar.ok { background: var(--crypto-green); box-shadow: 0 0 5px rgba(0, 255, 136, 0.3); } - .battery-bar.err { background: var(--crypto-red); } - - /* Mobile Monitor Specific (CRYPTO STYLE) */ + /* Mobile Monitor Specific (CRYPTO CANDLE STYLE) */ #mobile-monitor { - position: fixed; - top: 0; left: 0; right: 0; bottom: 0; - background: #000; - z-index: 2000; - display: none; - flex-direction: column; - padding: 5px; + position: fixed; top: 0; left: 0; right: 0; bottom: 0; + background: #000; z-index: 2000; display: none; + flex-direction: column; padding: 4px; } #mobile-monitor.active { display: flex; } .mm-header { - display: flex; - justify-content: space-between; - align-items: center; - background: #111; - padding: 8px 12px; - border-radius: 8px; - margin-bottom: 5px; - } - .mm-grid { - display: grid; - grid-template-columns: 1fr; - gap: 5px; - flex: 1; - overflow-y: auto; + display: flex; justify-content: space-between; align-items: center; + background: #111; padding: 6px 10px; border-radius: 6px; margin-bottom: 4px; } + .mm-grid { display: grid; grid-template-columns: 1fr; gap: 4px; flex: 1; overflow-y: auto; } .mm-row { - background: #080808; - padding: 10px; - border-radius: 8px; - border: 1px solid #1a1a1a; - display: flex; - flex-direction: column; - gap: 5px; + background: #080808; padding: 8px; border-radius: 6px; + border: 1px solid #1a1a1a; display: flex; flex-direction: column; gap: 4px; } - .mm-info { - display: flex; - justify-content: space-between; - align-items: center; - font-family: 'JetBrains Mono'; - } - .mm-url-text { font-size: 0.6rem; color: #888; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 60%; } + .mm-info { display: flex; justify-content: space-between; align-items: center; font-family: 'JetBrains Mono'; } + .mm-url-text { font-size: 0.55rem; color: #aaa; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 70%; } .mm-lat-text { font-size: 0.7rem; font-weight: bold; } .mm-spark-container { - height: 60px; - width: 100%; - position: relative; - background: rgba(255,255,255,0.02); - border-radius: 4px; - overflow: hidden; - } - .mm-canvas { - position: absolute; - top: 0; left: 0; width: 100%; height: 100%; + height: 80px; width: 100%; position: relative; + background: #050505; border-radius: 4px; overflow: hidden; } + .mm-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } - /* List Tables */ .data-table { width: 100%; border-collapse: collapse; } .data-table th { text-align: left; color: var(--text-dim); padding: 12px; border-bottom: 1px solid var(--border); font-size: 0.8rem; } .data-table td { padding: 12px; border-bottom: 1px solid var(--border); font-size: 0.9rem; } - .status-badge { padding: 4px 8px; border-radius: 4px; font-size: 0.7rem; font-weight: bold; text-transform: uppercase; } .bg-ok { background: rgba(0, 255, 136, 0.1); color: var(--crypto-green); } .bg-err { background: rgba(255, 51, 102, 0.1); color: var(--crypto-red); } - /* Inputs & Buttons */ - input, select { - background: #1e2329; - border: 1px solid var(--border); - color: #fff; - padding: 10px 12px; - border-radius: 4px; - outline: none; - width: 100%; - } - button { - padding: 10px 20px; - border-radius: 4px; - border: none; - font-weight: 700; - cursor: pointer; - transition: all 0.2s; - } + input, select { background: #1e2329; border: 1px solid var(--border); color: #fff; padding: 10px 12px; border-radius: 4px; outline: none; width: 100%; } + button { padding: 10px 20px; border-radius: 4px; border: none; font-weight: 700; cursor: pointer; transition: all 0.2s; } .btn-glow { background: var(--crypto-blue); color: #000; box-shadow: 0 0 15px rgba(0, 212, 255, 0.2); } - - /* Sections */ .section { display: none; } .section.active { display: block; } - /* Responsive */ + /* Floating Button for Mobile */ + .fab { + position: fixed; bottom: 20px; right: 20px; + width: 60px; height: 60px; border-radius: 50%; + background: var(--crypto-blue); color: #000; + display: none; align-items: center; justify-content: center; + box-shadow: 0 4px 20px rgba(0, 212, 255, 0.4); + z-index: 1000; cursor: pointer; font-size: 1.5rem; + } + @media (max-width: 768px) { aside { display: none; } .top-bar { padding: 0 1rem; } .content-area { padding: 1rem; } - .dashboard-grid { grid-template-columns: 1fr !important; } + .fab { display: flex; } + .top-candle-btn { display: flex !important; } } @@ -246,28 +167,31 @@ declare(strict_types=1);
- +
-
- [NETWORK HEALTH: 100%] [NODES ACTIVE: 12] +
+ +
+ [NET: 100%] +
-
- - 00:00:00 +
+ + 00:00:00
-
- Total Assets - 0 +
+ Click for Candle View + 0 Assets
Avg Latency @@ -279,14 +203,9 @@ declare(strict_types=1);
-
-

Asset Status (Battery View)

-
-
-

Latency Graph

- +
@@ -306,17 +225,19 @@ declare(strict_types=1);
-

Team Management

Manage your monitoring teams here.

-

API Keys

Generate keys for external integrations.

+

Team Management

Manage monitoring teams.

+

API Keys

External integrations.

- + +
🕯️
+
- REALTIME MONITOR + REALTIME CANDLES
@@ -324,9 +245,9 @@ declare(strict_types=1);
-
-
00:00:00
-
100% HEALTH
+
+
00:00:00
+
100% HEALTH
@@ -336,17 +257,32 @@ declare(strict_types=1); const sparklines = {}; class CryptoSparkline { - constructor(canvasId, initialData = []) { + constructor(canvasId) { this.canvas = document.getElementById(canvasId); this.ctx = this.canvas.getContext('2d'); - this.data = initialData.length ? initialData : Array(30).fill(0); - this.color = '#00ff88'; + this.data = []; + this.lastVal = Math.random() * 50 + 20; + // Pre-fill dummy data + for(let i=0; i<45; i++) { + this.addCandle(this.lastVal + (Math.random()*10-5), 'ok'); + } this.resize(); window.addEventListener('resize', () => this.resize()); } + addCandle(val, status) { + const open = this.lastVal; + const close = val; + const high = Math.max(open, close) + Math.random() * 4; + const low = Math.max(0, Math.min(open, close) - Math.random() * 4); + this.data.push({ open, close, high, low, ok: status === 'ok' }); + this.lastVal = val; + if (this.data.length > 55) this.data.shift(); + } + resize() { const rect = this.canvas.parentElement.getBoundingClientRect(); + if (!rect.width) return; this.canvas.width = rect.width * window.devicePixelRatio; this.canvas.height = rect.height * window.devicePixelRatio; this.ctx.scale(window.devicePixelRatio, window.devicePixelRatio); @@ -354,9 +290,7 @@ declare(strict_types=1); } update(val, status) { - this.data.push(val); - if (this.data.length > 50) this.data.shift(); - this.color = status === 'ok' ? '#00ff88' : '#ff3366'; + this.addCandle(val, status); this.draw(); } @@ -365,36 +299,47 @@ declare(strict_types=1); const h = this.canvas.height / window.devicePixelRatio; this.ctx.clearRect(0, 0, w, h); - if (this.data.length < 2) return; - - const min = Math.min(...this.data) * 0.8; - const max = Math.max(...this.data) * 1.2 || 100; - const range = max - min; - - this.ctx.beginPath(); - this.ctx.strokeStyle = this.color; - this.ctx.lineWidth = 2; - this.ctx.lineJoin = 'round'; - this.ctx.lineCap = 'round'; - - const step = w / (this.data.length - 1); - - for (let i = 0; i < this.data.length; i++) { - const x = i * step; - const y = h - ((this.data[i] - min) / range * h); - if (i === 0) this.ctx.moveTo(x, y); - else this.ctx.lineTo(x, y); + this.ctx.strokeStyle = '#111'; + this.ctx.lineWidth = 0.5; + for(let i=0; i [d.high, d.low]); + const minV = Math.min(...allVals) * 0.8; + const maxV = Math.max(...allVals) * 1.2 || 100; + const range = maxV - minV; + + const step = w / this.data.length; + const candleW = Math.max(2, step * 0.7); + + this.data.forEach((d, i) => { + const x = i * step + (step/2); + const color = d.ok ? '#00ff88' : '#ff3366'; + + const yO = h - ((d.open - minV) / range * h); + const yC = h - ((d.close - minV) / range * h); + const yH = h - ((d.high - minV) / range * h); + const yL = h - ((d.low - minV) / range * h); + + this.ctx.beginPath(); + this.ctx.strokeStyle = color; + this.ctx.lineWidth = 1; + this.ctx.moveTo(x, yH); + this.ctx.lineTo(x, yL); + this.ctx.stroke(); + + const bY = Math.min(yO, yC); + const bH = Math.max(1, Math.abs(yO - yC)); + + this.ctx.fillStyle = color; + this.ctx.fillRect(x - candleW/2, bY, candleW, bH); + }); } } @@ -419,6 +364,8 @@ declare(strict_types=1); function enterMobileMonitor() { document.getElementById('mobile-monitor').style.display = 'flex'; + // Force refresh sparklines + Object.values(sparklines).forEach(s => s.resize()); refreshData(); } @@ -428,7 +375,7 @@ declare(strict_types=1); } async function init() { - const resp = await fetch('api/uptime.php?action=settings'); + const resp = await fetch('api/uptime.php?action=settings&v='); const data = await resp.json(); isMonitoring = data.monitoring_enabled; updateStatusUI(); @@ -484,9 +431,9 @@ declare(strict_types=1); } async function refreshData() { - const statResp = await fetch('api/uptime.php?action=stats'); + const statResp = await fetch('api/uptime.php?action=stats&v='); const stats = await statResp.json(); - document.getElementById('stat-total').innerText = stats.total; + document.getElementById('stat-total').innerText = stats.total + ' Assets'; document.getElementById('stat-latency').innerText = Math.round(stats.avg_latency) + 'ms'; const health = stats.total > 0 ? ((stats.up / stats.total) * 100).toFixed(1) : 100; document.getElementById('stat-health').innerText = health + '%'; @@ -499,42 +446,25 @@ declare(strict_types=1); mainChart.update('none'); } - const listResp = await fetch('api/uptime.php?action=list'); + const listResp = await fetch('api/uptime.php?action=list&v='); const assets = await listResp.json(); const inventory = document.getElementById('inventory-list'); - const batteryRows = document.getElementById('battery-rows'); const mmCryptoGrid = document.getElementById('mm-crypto-grid'); inventory.innerHTML = ''; - batteryRows.innerHTML = ''; assets.forEach(u => { const isOk = u.last_status === 'ok'; inventory.innerHTML += `${u.url}${u.last_status}${u.last_latency}ms`; - // Dashboard Battery Rows - const brow = document.createElement('div'); - brow.style.marginBottom = '10px'; - brow.innerHTML = `
${u.url}${u.last_latency}ms
`; - const bcont = document.createElement('div'); - bcont.className = 'battery-container'; - for(let i=0; i<24; i++) { - const b = document.createElement('div'); - b.className = 'battery-bar' + (isOk ? ' ok' : ' err'); - bcont.appendChild(b); - } - brow.appendChild(bcont); - batteryRows.appendChild(brow); - - // Mobile Crypto Grid if (!document.getElementById(`mm-row-${u.id}`)) { const mrow = document.createElement('div'); mrow.id = `mm-row-${u.id}`; mrow.className = 'mm-row'; mrow.innerHTML = `
- ${u.url.split('//')[1]} + ${u.url.replace('https://','').replace('http://','')} ${u.last_latency}ms