Spaces:
Sleeping
Sleeping
Download static/script_v5.js from ParleDowntime/Pro: direct link, hf CLI and curl.
- Browser
- Download file 14.9 kB
-
https://huggingface.co/spaces/ParleDowntime/Pro/resolve/main/static/script_v5.js
- Command line
-
hf download hf://spaces/ParleDowntime/Pro/static/script_v5.js
-
curl -L -o script_v5.js https://huggingface.co/spaces/ParleDowntime/Pro/resolve/main/static/script_v5.js
14.9 kB
| /** | |
| * StatusPilot Ultra v6 — Fusion Engine | |
| * Best of all 3 dashboards combined | |
| */ | |
| let appData = { status: [], config: null }; | |
| let charts = {}; | |
| let currentLocationFilter = 'All'; | |
| let searchQuery = ''; | |
| let isAuthPromptShowing = false; | |
| // --- Axios 401 Interceptor --- | |
| axios.interceptors.response.use(r => r, error => { | |
| if (error.response && error.response.status === 401 && !isAuthPromptShowing) { | |
| isAuthPromptShowing = true; | |
| document.getElementById('master-auth-overlay').style.display = 'flex'; | |
| } | |
| return Promise.reject(error); | |
| }); | |
| // --- Init --- | |
| async function init() { | |
| updateClock(); | |
| setInterval(updateClock, 1000); | |
| const auth = await checkAuth(); | |
| if (auth.required && !auth.authenticated) { | |
| document.getElementById('master-auth-overlay').style.display = 'flex'; | |
| return; | |
| } | |
| unlock(); | |
| const lic = await checkLicense(); | |
| if (!lic.licensed) { | |
| document.getElementById('license-overlay').style.display = 'flex'; | |
| return; | |
| } | |
| await loadData(); | |
| setInterval(loadData, 30000); | |
| } | |
| async function loadData() { | |
| try { await Promise.all([loadConfig(), loadSummary(), loadStatus()]); } | |
| catch (e) { console.error("Sync:", e); } | |
| } | |
| async function checkAuth() { | |
| try { return (await axios.get('/api/auth/status')).data; } | |
| catch { return { required: true, authenticated: false }; } | |
| } | |
| function unlock() { | |
| isAuthPromptShowing = false; | |
| document.getElementById('master-auth-overlay').style.display = 'none'; | |
| const app = document.getElementById('main-app'); | |
| app.style.filter = 'none'; app.style.opacity = '1'; app.style.pointerEvents = 'auto'; | |
| } | |
| document.getElementById('master-login-btn').onclick = async () => { | |
| const key = document.getElementById('master-key-input').value.trim(); | |
| if (!key) return; | |
| try { | |
| const res = await axios.post('/api/auth/login', { key }); | |
| if (res.data.success) { | |
| setTimeout(async () => { | |
| const s = await checkAuth(); | |
| if (s.authenticated) { unlock(); await init(); } | |
| }, 500); | |
| } else { alert("Invalid Master Key"); } | |
| } catch { alert("Auth Node Offline"); } | |
| }; | |
| async function checkLicense() { | |
| try { return (await axios.get('/api/license/status')).data; } | |
| catch { return { licensed: false }; } | |
| } | |
| // --- Config --- | |
| async function loadConfig() { | |
| const res = await axios.get('/api/config'); | |
| appData.config = res.data; | |
| const locFilter = document.getElementById('location-filter'); | |
| const toolSelect = document.getElementById('test-tool-select'); | |
| const locs = new Set(['All', 'Global']); | |
| appData.config.services_to_monitor.forEach(s => { if(s.location) locs.add(s.location); }); | |
| const prev = locFilter.value; | |
| locFilter.innerHTML = ''; | |
| locs.forEach(l => { | |
| const o = document.createElement('option'); | |
| o.value = l; o.textContent = l === 'All' ? 'All Regions' : l; | |
| locFilter.appendChild(o); | |
| }); | |
| if (prev) locFilter.value = prev; | |
| toolSelect.innerHTML = ''; | |
| appData.config.services_to_monitor.forEach(s => { | |
| const o = document.createElement('option'); | |
| o.value = s.name; o.textContent = s.name; | |
| toolSelect.appendChild(o); | |
| }); | |
| renderManageList(); | |
| document.getElementById('check-interval').value = appData.config.check_interval; | |
| document.getElementById('alert-mode').value = appData.config.alert_mode || 'SMTP'; | |
| document.getElementById('smtp-user').value = appData.config.smtp_sender || ''; | |
| } | |
| function renderManageList() { | |
| const list = document.getElementById('manage-svc-list'); | |
| if (!list) return; | |
| list.innerHTML = ''; | |
| appData.config.services_to_monitor.forEach(s => { | |
| const item = document.createElement('div'); | |
| item.className = 'manage-item'; | |
| item.innerHTML = ` | |
| <div><b>${s.name}</b><br><span>${s.url}</span></div> | |
| <button class="btn-remove" data-name="${s.name}"><i class="fas fa-trash-alt"></i> Remove</button> | |
| `; | |
| list.appendChild(item); | |
| }); | |
| list.querySelectorAll('.btn-remove').forEach(btn => { | |
| btn.onclick = (e) => { | |
| const t = e.target.closest('.btn-remove'); | |
| if (t) deleteService(t.getAttribute('data-name')); | |
| }; | |
| }); | |
| } | |
| async function deleteService(name) { | |
| try { | |
| const res = await axios.post('/api/config/delete', { name }); | |
| if (res.data.status === 'success') { await loadConfig(); await loadStatus(); } | |
| else alert("Error: " + res.data.message); | |
| } catch (e) { alert("Delete failed."); } | |
| } | |
| // --- Summary --- | |
| async function loadSummary() { | |
| const s = (await axios.get('/api/summary')).data; | |
| document.getElementById('stat-monitors').textContent = s.total_monitors; | |
| document.getElementById('stat-uptime').textContent = s.uptime_pct + '%'; | |
| document.getElementById('stat-incidents').textContent = s.incidents; | |
| document.getElementById('stat-latency').textContent = s.avg_latency + 'ms'; | |
| } | |
| // --- Status --- | |
| async function loadStatus() { | |
| appData.status = (await axios.get('/api/status')).data; | |
| renderStatusGrid(); | |
| renderLatencyMatrix(); | |
| } | |
| function renderLatencyMatrix() { | |
| const container = document.getElementById('latency-matrix-bars'); | |
| if (!container) return; | |
| container.innerHTML = ''; | |
| const sorted = [...appData.status].sort((a, b) => b.latency - a.latency).slice(0, 8); | |
| const max = Math.max(...sorted.map(s => s.latency), 100); | |
| sorted.forEach(s => { | |
| const pct = Math.min((s.latency / max) * 100, 100); | |
| const row = document.createElement('div'); | |
| row.className = 'latency-row'; | |
| row.innerHTML = ` | |
| <span class="name">${s.name}</span> | |
| <div class="bar-track"><div class="bar-fill" style="width:${pct}%"></div></div> | |
| <span class="ms">${s.latency}ms</span> | |
| `; | |
| container.appendChild(row); | |
| }); | |
| } | |
| function renderStatusGrid() { | |
| const grid = document.getElementById('status-grid'); | |
| grid.innerHTML = ''; | |
| const filtered = appData.status.filter(s => { | |
| const locOk = currentLocationFilter === 'All' || s.location === currentLocationFilter; | |
| const searchOk = !searchQuery || s.name.toLowerCase().includes(searchQuery.toLowerCase()); | |
| return locOk && searchOk; | |
| }); | |
| filtered.forEach(s => { | |
| const row = document.createElement('div'); | |
| row.className = 'svc-row'; | |
| row.onclick = () => openDetails(s.name); | |
| let barsHTML = '<div class="uptime-bars">'; | |
| const history = [...Array(Math.max(0, 30 - s.timeline.length)).fill(null), ...s.timeline]; | |
| history.forEach(state => { | |
| const cls = state === null ? 'none' : (state ? 'up' : 'down'); | |
| barsHTML += `<div class="bar ${cls}"></div>`; | |
| }); | |
| barsHTML += '</div>'; | |
| const scopeClass = s.location === 'Security Node' ? 'security' : (s.location === 'Primary Web' ? 'web' : 'global'); | |
| row.innerHTML = ` | |
| <div><div class="svc-name">${s.name}</div><div class="svc-url">${s.url}</div></div> | |
| <div><span class="scope-badge ${scopeClass}">${s.location}</span></div> | |
| ${barsHTML} | |
| <div class="status-label ${s.is_up ? 'up' : 'down'}">${s.is_up ? 'ONLINE' : 'DOWN'}</div> | |
| <div class="latency-chip">${s.latency}ms</div> | |
| `; | |
| grid.appendChild(row); | |
| }); | |
| } | |
| // --- Modal --- | |
| async function openDetails(name) { | |
| document.getElementById('modal-title').textContent = name; | |
| document.getElementById('modal-subtitle').textContent = `Historical performance data for ${name}`; | |
| document.getElementById('matrix-modal').classList.add('open'); | |
| try { | |
| const res = await axios.get(`/api/history/${encodeURIComponent(name)}`); | |
| renderModalCharts(res.data); | |
| } catch (e) { console.error(e); } | |
| } | |
| function renderModalCharts(data) { | |
| const labels = data.map(d => new Date(d.timestamp).toLocaleTimeString()); | |
| const latencies = data.map(d => d.latency); | |
| const uptimes = data.map(d => d.is_up ? 100 : 0); | |
| if (charts.detLat) charts.detLat.destroy(); | |
| if (charts.detUp) charts.detUp.destroy(); | |
| if (charts.detErr) charts.detErr.destroy(); | |
| const base = { | |
| responsive: true, maintainAspectRatio: false, | |
| plugins: { legend: { display: false } }, | |
| scales: { | |
| x: { display: false }, | |
| y: { grid: { color: 'rgba(255,255,255,0.04)' }, ticks: { color: '#64748b', font: { size: 9 } } } | |
| } | |
| }; | |
| charts.detUp = new Chart(document.getElementById('uptimeChart'), { | |
| type: 'line', | |
| data: { labels, datasets: [{ data: uptimes, borderColor: '#10b981', backgroundColor: 'rgba(16,185,129,0.08)', fill: true, stepped: true, pointRadius: 0, borderWidth: 1.5 }] }, | |
| options: { ...base, scales: { ...base.scales, y: { ...base.scales.y, max: 105, min: -5 } } } | |
| }); | |
| charts.detLat = new Chart(document.getElementById('latencyChart'), { | |
| type: 'line', | |
| data: { labels, datasets: [{ data: latencies, borderColor: '#0ea5e9', backgroundColor: 'rgba(14,165,233,0.08)', fill: true, tension: 0.4, pointRadius: 0, borderWidth: 1.5 }] }, | |
| options: base | |
| }); | |
| const upCount = data.filter(d => d.is_up).length; | |
| const downCount = data.filter(d => !d.is_up).length; | |
| charts.detErr = new Chart(document.getElementById('errorChart'), { | |
| type: 'doughnut', | |
| data: { labels: ['Operational', 'Issues'], datasets: [{ data: [upCount, downCount], backgroundColor: ['#10b981', '#1e293b'], borderWidth: 0 }] }, | |
| options: { cutout: '75%', plugins: { legend: { position: 'bottom', labels: { color: '#64748b', padding: 12, font: { size: 10 } } } } } | |
| }); | |
| renderIncidents(data); | |
| } | |
| function renderIncidents(data) { | |
| const log = document.getElementById('incident-log'); | |
| if (!log) return; | |
| log.innerHTML = ''; | |
| const fails = data.filter(d => !d.is_up).reverse().slice(0, 10); | |
| if (fails.length === 0) { | |
| log.innerHTML = '<div style="text-align:center; padding:1.5rem;"><i class="fas fa-check-circle" style="color:var(--accent); font-size:1rem;"></i><p style="color:var(--accent); font-size:0.72rem; font-weight:700; margin-top:6px;">✓ SYSTEM IS NOMINAL</p></div>'; | |
| return; | |
| } | |
| fails.forEach(f => { | |
| const el = document.createElement('div'); | |
| el.style.cssText = 'display:flex; justify-content:space-between; align-items:center; padding:6px 10px; margin-bottom:4px; background:rgba(244,63,94,0.06); border-left:3px solid var(--danger); border-radius:4px;'; | |
| const msg = f.message || "Connection Error"; | |
| el.innerHTML = `<div><span style="color:var(--danger); font-size:0.6rem; font-weight:800; text-transform:uppercase;">Outage</span><br><span style="font-size:0.7rem; color:#fff;">${msg}</span></div><span style="font-size:0.65rem; color:var(--text-dim); font-weight:600;">${new Date(f.timestamp).toLocaleTimeString()}</span>`; | |
| log.appendChild(el); | |
| }); | |
| } | |
| function updateClock() { | |
| document.getElementById('live-clock').textContent = new Date().toLocaleTimeString(); | |
| } | |
| // --- Event Handlers --- | |
| // Modal close | |
| document.getElementById('modal-close-btn').onclick = () => document.getElementById('matrix-modal').classList.remove('open'); | |
| document.getElementById('matrix-modal').onclick = (e) => { if (e.target === document.getElementById('matrix-modal')) document.getElementById('matrix-modal').classList.remove('open'); }; | |
| // Navigation | |
| document.querySelectorAll('.sidebar-nav a').forEach(item => { | |
| item.onclick = (e) => { | |
| e.preventDefault(); | |
| document.querySelectorAll('.sidebar-nav a').forEach(n => n.classList.remove('active')); | |
| item.classList.add('active'); | |
| const target = item.id.replace('nav-', 'section-'); | |
| document.querySelectorAll('.content-section').forEach(s => s.style.display = 'none'); | |
| document.getElementById(target).style.display = 'block'; | |
| }; | |
| }); | |
| // Location filter | |
| document.getElementById('location-filter').onchange = (e) => { currentLocationFilter = e.target.value; renderStatusGrid(); }; | |
| // Search | |
| document.getElementById('search-services').oninput = (e) => { searchQuery = e.target.value; renderStatusGrid(); }; | |
| // M365 | |
| document.getElementById('activate-m365-btn').onclick = async () => { | |
| const sender = document.getElementById('m365-sender').value.trim(); | |
| const pass = document.getElementById('m365-pass').value.trim(); | |
| const recipient = document.getElementById('m365-recipient').value.trim(); | |
| if (!sender || !pass || !recipient) return alert("All M365 fields required."); | |
| const config = { ...appData.config }; | |
| config.services_to_monitor.push({ name: "M365 Mail Flow", url: "https://outlook.office365.com", location: "Global", type: "m365_mail", m365_sender: sender, m365_password: pass, m365_recipient: recipient }); | |
| try { await axios.post('/api/config', config); alert("M365 Monitor Activated!"); loadConfig(); } | |
| catch { alert("Failed to activate."); } | |
| }; | |
| // Add service | |
| document.getElementById('add-svc-btn').onclick = async () => { | |
| const name = document.getElementById('new-svc-name').value.trim(); | |
| const url = document.getElementById('new-svc-url').value.trim(); | |
| const loc = document.getElementById('new-svc-loc').value.trim() || 'Global'; | |
| const type = document.getElementById('new-svc-type').value; | |
| if (!name || !url) return alert("Name and URL required."); | |
| const config = { ...appData.config }; | |
| config.services_to_monitor.push({ name, url, location: loc, type }); | |
| try { await axios.post('/api/config', config); alert("Node Provisioned!"); document.getElementById('new-svc-name').value = ''; document.getElementById('new-svc-url').value = ''; loadConfig(); loadStatus(); } | |
| catch { alert("Error."); } | |
| }; | |
| // Save settings | |
| document.getElementById('save-settings-btn').onclick = async () => { | |
| const config = { ...appData.config }; | |
| config.check_interval = parseInt(document.getElementById('check-interval').value); | |
| config.alert_mode = document.getElementById('alert-mode').value; | |
| config.smtp_sender = document.getElementById('smtp-user').value; | |
| config.smtp_password = document.getElementById('smtp-pass').value; | |
| try { await axios.post('/api/config', config); alert("Config Saved!"); loadConfig(); } | |
| catch { alert("Save failed."); } | |
| }; | |
| // Purge | |
| const purgeBtn = document.getElementById('purge-stale-btn'); | |
| if (purgeBtn) purgeBtn.onclick = async () => { try { const r = await axios.post('/api/admin/purge_stale'); alert(`Purged ${r.data.purged_records} records.`); loadData(); } catch { alert("Purge failed."); } }; | |
| // Export | |
| const exportBtn = document.getElementById('download-excel-btn'); | |
| if (exportBtn) exportBtn.onclick = () => window.location.href = '/api/report/excel'; | |
| init(); | |