sleekside-harmony / index.html
sukritvemula's picture
Manual changes saved
d1ca34a verified
Raw
History Blame Contribute Delete
17.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Live Status & Speed Test</title>
<script src="https://cdn.tailwindcss.com"></script>
<meta name="color-scheme" content="light dark" />
<style>
:root { --bg:#f8fafc; --card:#ffffff; --muted:#64748b; --ink:#0f172a; --ok:#16a34a; --warn:#f59e0b; --bad:#ef4444; --accent:#2563eb; --row:#f1f5f9; }
@media (prefers-color-scheme: dark){ :root{ --bg:#0b1220; --card:#0f172a; --muted:#94a3b8; --ink:#e2e8f0; --row:#0b1326; } }
body{ background:var(--bg); color:var(--ink) }
table{ width:100%; border-collapse:collapse; font-size:.95rem }
th,td{ padding:8px 10px; text-align:left; vertical-align:middle }
th{ background:color-mix(in srgb, var(--row) 90%, transparent); font-weight:700; position:sticky; top:0; z-index:1 }
tr:nth-child(even){ background:var(--row) }
.status-dot{ width:10px; height:10px; border-radius:999px; display:inline-block; margin-right:.5rem }
.trend{ width:140px; height:28px; display:block }
.mono{ font-variant-numeric:tabular-nums }
.btn{ background:var(--accent); color:#fff; border-radius:9999px; padding:.6rem .9rem; font-weight:700 }
.btn:disabled{ opacity:.6; cursor:not-allowed }
.ad-box{ background:var(--card); border:1px dashed #94a3b8; height:90px; display:flex; align-items:center; justify-content:center; color:var(--muted) }
.pill{ padding:.15rem .5rem; border-radius:9999px; font-size:.75rem; font-weight:700 }
.pill-ok{ background: color-mix(in srgb, var(--ok) 18%, transparent); color:var(--ok) }
.pill-warn{ background: color-mix(in srgb, var(--warn) 18%, transparent); color:var(--warn) }
.pill-bad{ background: color-mix(in srgb, var(--bad) 18%, transparent); color:var(--bad) }
</style>
</head>
<body class="antialiased">
<div class="p-4 max-w-7xl mx-auto">
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 border-b border-slate-300/40 pb-3 mb-4">
<div>
<h1 class="text-2xl font-extrabold">Live Service Status</h1>
<p class="text-sm" style="color:var(--muted)">Free endpoints, Resource Timing where allowed, with safe fallbacks</p>
<p class="text-xs mono" id="lastUpdated">Updated β€”</p>
</div>
<div class="flex items-center gap-4">
<div class="text-sm">
<div class="font-semibold">Speed Test</div>
<div class="mono text-xl"><span id="dlMbps">0.00</span> Mbps ↓ β€’ <span id="ulMbps">0.00</span> Mbps ↑</div>
<div class="text-xs mono" style="color:var(--muted)">Latency <span id="latency">β€”</span> ms β€’ Jitter <span id="jitter">β€”</span> ms β€’ Loss <span id="loss">β€”</span>%</div>
</div>
<button id="runSpeedTest" class="btn">Run test</button>
</div>
</div>
<div class="flex gap-3 text-xs mb-3">
<span class="pill pill-ok">Operational</span>
<span class="pill pill-warn">Issues</span>
<span class="pill pill-bad">Outage</span>
</div>
<div class="ad-box rounded mb-4"><span class="text-sm">Ad Placeholder (728Γ—90)</span></div>
<div class="overflow-x-auto rounded border border-slate-300/40 bg-[color:var(--card)]">
<table>
<thead>
<tr>
<th class="w-1/3">Service</th>
<th class="w-1/3">Status</th>
<th class="w-1/3">Ping Trend</th>
</tr>
</thead>
<tbody id="serviceStatus">
<!-- placeholder rows so it’s never blank -->
</tbody>
</table>
</div>
<div class="ad-box rounded my-4"><span class="text-sm">Ad Placeholder (Responsive)</span></div>
<div class="grid md:grid-cols-3 gap-3">
<div class="p-3 rounded border border-slate-300/40 bg-[color:var(--card)]">
<div class="font-semibold mb-1">Protocol & cache</div>
<div class="text-sm mono" id="protoInfo">β€”</div>
</div>
<div class="p-3 rounded border border-slate-300/40 bg-[color:var(--card)]">
<div class="font-semibold mb-1">Transfer stats</div>
<div class="text-sm mono" id="xferInfo">β€”</div>
</div>
<div class="p-3 rounded border border-slate-300/40 bg-[color:var(--card)]">
<div class="font-semibold mb-1">Timing breakdown</div>
<div class="text-sm mono" id="timingInfo">β€”</div>
</div>
</div>
<footer class="mt-8 text-xs" style="color:var(--muted)">
<p>Uses CORS-permissive, no-key endpoints and image pings with cache-busting; results vary by CDN and device load.</p>
<p>Β© 2025 Live Status.</p>
</footer>
</div>
<script>
(() => {
// ---------- Safe, CORS-aware foundations ----------
const $ = s => document.querySelector(s);
const sleep = ms => new Promise(r => setTimeout(r, ms));
const clamp = (v,a,b)=>Math.min(b,Math.max(a,v));
const avg = a => a.length ? a.reduce((x,y)=>x+y,0)/a.length : 0;
const med = a => { if(!a.length) return 0; const b=[...a].sort((x,y)=>x-y); const m=b.length>>1; return b.length%2?b[m]:(b[m-1]+b[m])/2; };
const stdev = a => { const m=avg(a); return Math.sqrt(avg(a.map(x=>(x-m)**2))||0); };
const now = () => performance.now();
const key = () => crypto.getRandomValues(new Uint32Array(1))[0].toString();
const withCB = (u, extra={}) => { const url=new URL(u); url.searchParams.set('cb',key()); for(const [k,v] of Object.entries(extra)) url.searchParams.set(k,v); return url.toString(); };
// Only use fetch for endpoints that send CORS headers; otherwise use Image ping
const CORS_GET = ['https://httpbin.org/get'];
const CORS_POST = ['https://httpbin.org/post', 'https://postman-echo.com/post'];
// Cloudflare test files are widely CORS-enabled; if blocked, code falls back automatically
const DL_FILES = [
'https://speed.cloudflare.com/__down?bytes=1048576', // ~1MB
'https://speed.cloudflare.com/__down?bytes=5242880', // ~5MB
'https://speed.cloudflare.com/__down?bytes=10485760' // ~10MB
];
// Performance observer (optional, not required for UI)
const perfMap = new Map();
try {
const po = new PerformanceObserver(list => { for (const e of list.getEntries()) if (e.entryType==='resource') perfMap.set(e.name, e); });
po.observe({ type:'resource', buffered:true });
} catch {}
const fetchTimed = async (url, opts={}, timeout=15000) => {
const ctrl = new AbortController();
const t0 = now();
const to = setTimeout(()=>ctrl.abort(), timeout);
try {
const res = await fetch(url, { cache:'no-store', mode:'cors', ...opts, signal: ctrl.signal });
const buf = await res.arrayBuffer().catch(()=>new ArrayBuffer(0));
clearTimeout(to);
const entry = perfMap.get(url) || performance.getEntriesByName(url, 'resource')[0] || null;
return { ok: res.ok, ms: now()-t0, bytes: buf.byteLength, entry };
} catch (e) {
clearTimeout(to);
return { ok:false, ms: now()-t0, bytes:0, entry:null };
}
};
const imgPing = async (url, timeout=5000) => {
const t0 = now();
const img = new Image();
img.referrerPolicy = 'no-referrer';
const p = new Promise(res => {
const done = ok => res({ ok, ms: Math.round(now()-t0) });
img.onload = () => done(true);
img.onerror = () => done(false);
});
img.src = withCB(url);
const to = sleep(timeout).then(()=>({ ok:false, ms: timeout }));
return Promise.race([p, to]);
};
const assetFor = name => {
switch (name.toLowerCase()) {
case 'google': return 'https://www.google.com/favicon.ico';
case 'youtube': return 'https://www.youtube.com/favicon.ico';
case 'facebook': return 'https://www.facebook.com/favicon.ico';
case 'instagram': return 'https://www.instagram.com/favicon.ico';
case 'x (twitter)': return 'https://twitter.com/favicon.ico';
case 'whatsapp': return 'https://web.whatsapp.com/favicon.ico';
case 'jio': return 'https://www.jio.com/assets/images/favicon.ico';
case 'airtel': return 'https://www.airtel.in/favicon.ico';
case 'amazon': return 'https://www.amazon.in/favicon.ico';
case 'netflix': return 'https://www.netflix.com/favicon.ico';
case 'discord': return 'https://discord.com/assets/847541504914fd33810e70a0ea73177e.ico';
default: return 'https://www.google.com/favicon.ico';
}
};
const services = [
"Airtel","Jio","Instagram","Gmail","YouTube","Whatsapp","Flipkart","BSNL","ACT",
"Google","Snapchat","Roblox","Steam","Telegram","Facebook","Amazon","Netflix","Discord",
"IRCTC","Swiggy","Google Cloud","OpenAI","App Store","Microsoft 365"
];
const history = Object.fromEntries(services.map(s=>[s,[]]));
const reliability = Object.fromEntries(services.map(s=>[s,1]));
const MAX_POINTS = 30;
// Pre-render placeholder rows so table is never blank
(() => {
const frag = document.createDocumentFragment();
for (const s of services) {
const tr = document.createElement('tr');
tr.innerHTML = `
<td class="flex items-center"><span class="status-dot" style="background:var(--warn)"></span>${s}</td>
<td class="mono" style="color:var(--muted);font-weight:700">Checking…</td>
<td><svg class="trend" viewBox="0 0 140 28" preserveAspectRatio="none"><polyline points="0,14 140,14" fill="none" stroke="var(--muted)" stroke-width="1"/></svg></td>
`;
frag.appendChild(tr);
}
$('#serviceStatus').replaceChildren(frag);
})();
const statusFrom = (ms, okRatio) => {
if (okRatio < 0.6) return { label:'Outage', color:'var(--bad)' };
if (ms < 800 && okRatio > 0.9) return { label:'Operational', color:'var(--ok)' };
if (ms < 2500) return { label:'Issues', color:'var(--warn)' };
return { label:'Outage', color:'var(--bad)' };
};
const renderTrend = times => {
if (!times.length) return `<svg class="trend" viewBox="0 0 140 28" preserveAspectRatio="none"><polyline points="0,14 140,14" fill="none" stroke="var(--muted)" stroke-width="1"/></svg>`;
const w=140,h=28,maxMs=5000;
const step = times.length>1 ? w/(times.length-1) : w;
let pts='';
for (let i=0;i<times.length;i++){
const x=(i*step).toFixed(2);
const y=(h - clamp(times[i],0,maxMs)/maxMs*h).toFixed(2);
pts+=`${x},${y} `;
}
const last = times[times.length-1];
const color = last < 2000 ? 'var(--ok)' : last < 5000 ? 'var(--warn)' : 'var(--bad)';
return `<svg class="trend" viewBox="0 0 ${w} ${h}" preserveAspectRatio="none" aria-hidden="true">
<polyline points="${pts.trim()}" fill="none" stroke="${color}" stroke-width="2"/>
</svg>`;
};
const updateServices = async () => {
const rows = [];
for (const s of services) {
// Probe both: favicon via Image (no CORS needed), and a CORS GET we can time
let imgRes = { ok:false, ms:5000 }, getRes = { ok:false, ms:5000 };
try { imgRes = await imgPing(assetFor(s), 5000); } catch {}
try { getRes = await fetchTimed(withCB(CORS_GET[0]), {}, 5000); } catch {}
const ok = imgRes.ok || getRes.ok;
const t = med([imgRes.ms, getRes.ms].filter(Number.isFinite));
const hist = history[s];
hist.push(t || 5000);
if (hist.length > MAX_POINTS) hist.shift();
reliability[s] = 0.8*reliability[s] + 0.2*(ok ? 1 : 0);
const { label, color } = statusFrom(t || 5000, reliability[s]);
rows.push({ name:s, ms: Math.round(t||5000), label, color, hist:[...hist] });
// yield to UI
if ('requestIdleCallback' in window) await new Promise(res=>requestIdleCallback(res,{timeout:12}));
else await sleep(0);
}
// Render batch
const frag = document.createDocumentFragment();
for (const r of rows) {
const tr = document.createElement('tr');
tr.innerHTML = `
<td class="flex items-center"><span class="status-dot" style="background:${r.color}"></span>${r.name}</td>
<td class="mono" style="color:${r.color};font-weight:700">${r.label} (${r.ms} ms)</td>
<td>${renderTrend(r.hist)}</td>
`;
frag.appendChild(tr);
}
$('#serviceStatus').replaceChildren(frag);
$('#lastUpdated').textContent = `Updated ${new Date().toLocaleTimeString()}`;
};
updateServices();
setInterval(updateServices, 60000);
// ---------- Speed Test with guaranteed fallback ----------
const tween = (el, from, to, dur=600) => {
from = Number(from)||0; to = Number(to)||0;
const t0 = performance.now();
const step = t => {
const p = clamp((t - t0)/dur, 0, 1);
const v = from + (to - from) * (1 - Math.cos(Math.PI*p))/2;
el.textContent = v.toFixed(2);
if (p < 1) requestAnimationFrame(step);
};
requestAnimationFrame(step);
};
const latencyJitter = async (n=7) => {
const samples=[]; let fails=0;
for (let i=0;i<n;i++){
try {
const r = await fetchTimed(withCB('https://www.google.com/favicon.ico'), {}, 4000);
if (r.ok) samples.push(r.ms); else fails++;
} catch { fails++; }
await sleep(50 + Math.random()*70);
}
return {
latency: samples.length ? Math.round(med(samples)) : 999,
jitter: Math.round(stdev(samples)),
loss: Math.round(100 * (fails / (samples.length + fails || 1)))
};
};
const downloadFetch = async () => {
// Try CORS-enabled Cloudflare test files; gracefully degrade if blocked
const seq = [DL_FILES[0], DL_FILES[1]];
let accBytes = 0, accMs = 0, entries=[];
for (const u of seq) {
const r = await fetchTimed(withCB(u), {}, 20000);
if (r.ok) { accBytes += r.bytes; accMs = Math.max(accMs, r.ms); if (r.entry) entries.push(r.entry); }
}
// If too fast, refine with a bigger file
if (accMs && accMs < 1500) {
const r = await fetchTimed(withCB(DL_FILES[2]), {}, 20000);
if (r.ok) { accBytes += r.bytes; accMs = Math.max(accMs, r.ms); if (r.entry) entries.push(r.entry); }
}
return { ok: accBytes>0 && accMs>0, mbps: accMs ? (accBytes*8)/accMs/1000 : 0, entries };
};
const downloadFallbackImage = async () => {
// Stream an image; only start/end timing available, but works everywhere
const t0 = now();
const img = new Image();
const done = new Promise(res=>{
img.onload = ()=>res({ ok:true, ms: now()-t0, bytes: 500000 }); // rough img size proxy
img.onerror = ()=>res({ ok:false, ms: now()-t0, bytes: 0 });
});
img.src = withCB('https://www.gstatic.com/webp/gallery/1.jpg'); // generally accessible
const r = await Promise.race([done, sleep(15000).then(()=>({ ok:false, ms:15000, bytes:0 }))]);
return { ok:r.ok, mbps: r.ms ? (r.bytes*8)/r.ms/1000 : 0, entries:[] };
};
const uploadFetch = async () => {
// Find a working no-key POST endpoint with CORS
let endpoint = null;
for (const ep of CORS_POST) {
try {
const t = await fetchTimed(withCB(ep), { method:'POST', body: new Uint8Array(1) }, 5000);
if (t.ok) { endpoint = ep; break; }
} catch {}
}
if (!endpoint) return { ok:false, mbps:0, entries:[] };
const total = 2*1024*1024;
const parts = 2;
const chunk = Math.floor(total/parts);
const make = () => { const a = new Uint8Array(chunk); crypto.getRandomValues(a); return a; };
const tasks = Array.from({length:parts}, () => fetchTimed(withCB(endpoint), {
method:'POST',
headers:{ 'Content-Type':'application/octet-stream' },
body: make()
}, 15000));
const res = await Promise.allSettled(tasks);
const vals = res.map(v=>v.value || { ok:false, ms:0, entry:null });
const oks = vals.filter(v=>v.ok);
const sent = oks.reduce((a,b)=> a + (b.entry?.encodedBodySize || chunk), 0);
const ms = Math.max(...oks.map(v=>v.ms).concat([0]));
return { ok: ms>0 && sent>0, mbps: ms ? (sent*8)/ms/1000 : 0, entries: oks.map(v=>v.entry).filter(Boolean) };
};
const runSpeed = async () => {
const btn = $('#runSpeedTest'); btn.disabled = true;
const lj = await latencyJitter(8).catch(()=>({latency:999,jitter:0,loss:100}));
$('#latency').textContent = lj.latency;
$('#jitter').textContent = lj.jitter;
$('#loss').textContent = lj.loss;
// Download: try CORS-first, then fallback image
let dl = await downloadFetch().catch(()=>({ok:false,mbps:0,entries:[]}));
if (!dl.ok) dl = await downloadFallbackImage();
tween($('#dlMbps'), $('#dlMbps').textContent, dl.mbps);
// Upload: may be blocked on some networks; handle gracefully
let ul = await uploadFetch().catch(()=>({ok:false,mbps:0,entries:[]}));
tween($('#ulMbps'), $('#ulMbps').textContent, ul.mbps);
// Diagnostics (best-effort)
const e = (dl.entries&&dl.entries[0]) || (ul.entries&&ul.entries[0]) || null;
if (e) {
const dns = Math.max(0,(e.domainLookupEnd||0)-(e.domainLookupStart||0));
const conn= Math.max(0,(e.connectEnd||0)-(e.connectStart||0));
const tls = Math.max(0,(e.requestStart||0)-(e.secureConnectionStart||0));
const req = Math.max(0,(e.responseStart||0)-(e.requestStart||0));
const fetchT = Math.max(0,(e.responseEnd||0)-(e.fetchStart||0));
$('#protoInfo').textContent = `nextHop=${e.nextHopProtocol||'β€”'} β€’ cache=${(e.transferSize===0)?'HIT':'MISS'}`;
$('#xferInfo').textContent = `transfer=${e.transferSize||0}B β€’ encoded=${e.encodedBodySize||0}B β€’ decoded=${e.decodedBodySize||0}B`;
$('#timingInfo').textContent = `dns=${dns|0}ms β€’ conn=${conn|0}ms β€’ tls=${tls|0}ms β€’ req=${req|0}ms β€’ fetch=${fetchT|0}ms`;
} else {
$('#protoInfo').textContent = 'nextHop=β€” β€’ cache=β€”';
$('#xferInfo').textContent = 'transfer=β€” β€’ encoded=β€” β€’ decoded=β€”';
$('#timingInfo').textContent = 'β€”';
}
setTimeout(()=>{ btn.disabled = false; }, 1200);
};
$('#runSpeedTest').addEventListener('click', runSpeed);
})();
</script>
</body>
</html>