LEADERBOARDS
+// LIVE OPERATIONAL RANKINGS β DERIVED FROM NGINX ACCESS LOGS
+ +diff --git a/api/data/changelog.json b/api/data/changelog.json index d0a563a..da016f0 100644 --- a/api/data/changelog.json +++ b/api/data/changelog.json @@ -1,6 +1,27 @@ { "site": "jaeswift.xyz", "entries": [ + { + "version": "1.37.2", + "date": "20/04/2026", + "category": "FEATURE", + "title": "LEADERBOARDS β Live Operational Rankings", + "changes": [ + "New page /hq/leaderboards with 6-panel grid showing real-time traffic intelligence pulled from nginx access logs", + "TOP COUNTRIES (24h): GeoIP-resolved top 15 origins with auto-generated flag emojis and animated ranked bars", + "TOP PAGES (24h): most-hit routes (API/CSS/JS/favicons filtered out), top 20 with green-code path display", + "TOP REFERRERS (7d): inbound link sources filtered for own domain β graceful empty state for direct-traffic sites", + "PEAK HOURS (24h UTC): 24-bar hourly chart with amber-highlighted peak column, hover tooltips, auto-summary showing peak hour + total req/24h", + "BROWSER BREAKDOWN (24h): top 10 UA families with per-browser emoji (π’ Chrome / π¦ Firefox / π§ Safari / π Edge / π€ Bot)", + "OPERATOR LEADERBOARD (7d): top 10 most-active IPs (privacy-masked first/last octet), request counts, last-seen relative timestamps β self-IP highlighted with amber (YOU) badge", + "Gold/silver/bronze rank styling for top-3 rows across all panels; #01β#NN zero-padded ranks elsewhere", + "60s auto-refresh + manual refresh button + live LAST UPDATED counter (ticks per second)", + "Backend /api/leaderboards endpoint returns JSON aggregating countries, pages, referrers, peak_hours, browsers, top_operators β 60s in-memory cache", + "New files: hq/leaderboards.html Β· css/leaderboards.css Β· js/leaderboards.js", + "Nav updated: HQ menu now includes LEADERBOARDS link with description \"Live traffic rankings & operator stats\"", + "Fully mobile responsive (768px stack / 420px compact) with scanlines + grid-bg matching site theme" + ] + }, { "version": "1.37.1", "date": "20/04/2026", diff --git a/api/data/navigation.json b/api/data/navigation.json index 1cbd5ec..e04a171 100644 --- a/api/data/navigation.json +++ b/api/data/navigation.json @@ -23,6 +23,11 @@ "label": "MAINTENANCE LOG", "url": "/hq/logs", "description": "Operational logs & diagnostics" + }, + { + "label": "LEADERBOARDS", + "url": "/hq/leaderboards", + "description": "Live traffic rankings & operator stats" } ] }, diff --git a/css/leaderboards.css b/css/leaderboards.css new file mode 100644 index 0000000..4a5a444 --- /dev/null +++ b/css/leaderboards.css @@ -0,0 +1,265 @@ +/* =================================================== + JAESWIFT.XYZ β LEADERBOARDS + =================================================== */ + +.lb-meta { + display: flex; + flex-wrap: wrap; + gap: 10px; + align-items: center; + margin-top: 18px; + font-family: var(--font-mono); + font-size: 11px; +} + +.lb-badge { + padding: 6px 12px; + background: var(--bg-panel, #161616); + border: 1px solid var(--border-color, #2a2a2a); + color: var(--text-secondary, #aaa); + letter-spacing: 1.5px; + text-transform: uppercase; + border-radius: 2px; +} +.lb-badge.lb-live { + color: var(--status-green, #00cc33); + border-color: rgba(0, 204, 51, 0.3); + box-shadow: 0 0 10px rgba(0, 204, 51, 0.15); +} + +.lb-refresh-btn { + padding: 6px 14px; + background: transparent; + border: 1px solid var(--status-green, #00cc33); + color: var(--status-green, #00cc33); + font-family: var(--font-mono); + font-size: 11px; + letter-spacing: 1.5px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + margin-left: auto; +} +.lb-refresh-btn:hover { + background: rgba(0, 204, 51, 0.1); + box-shadow: 0 0 12px rgba(0, 204, 51, 0.4); +} + +/* βββ Grid βββ */ +.lb-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); + gap: 18px; + margin-top: 28px; +} + +.lb-panel { + background: var(--bg-panel, #161616); + border: 1px solid var(--border-color, #2a2a2a); + border-radius: 2px; + overflow: hidden; + position: relative; +} +.lb-panel::before { + content: ''; + position: absolute; + top: 0; left: 0; width: 3px; height: 100%; + background: linear-gradient(180deg, var(--status-green, #00cc33) 0%, transparent 100%); + opacity: 0.6; +} + +.lb-panel-header { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 16px; + background: rgba(0, 0, 0, 0.35); + border-bottom: 1px solid var(--border-color, #2a2a2a); +} +.lb-panel-icon { font-size: 16px; } +.lb-panel-header h2 { + flex: 1; + margin: 0; + font-family: var(--font-display, 'Share Tech Mono'), 'Orbitron', monospace; + font-size: 13px; + letter-spacing: 2px; + color: var(--text-primary, #e0e0e0); + text-transform: uppercase; +} +.lb-panel-period { + font-family: var(--font-mono); + font-size: 10px; + color: var(--warning, #c9a227); + letter-spacing: 1.5px; + padding: 2px 8px; + border: 1px solid rgba(201, 162, 39, 0.3); + border-radius: 2px; +} + +.lb-panel-body { + padding: 14px 16px; + font-family: var(--font-mono); + min-height: 220px; + max-height: 420px; + overflow-y: auto; +} +.lb-loading { + color: var(--text-secondary, #888); + font-size: 12px; + text-align: center; + padding: 40px 0; + letter-spacing: 1px; + opacity: 0.65; + animation: lb-pulse 1.4s ease-in-out infinite; +} +@keyframes lb-pulse { + 0%,100% { opacity: 0.45; } + 50% { opacity: 0.85; } +} + +/* βββ Rows (generic bar chart row) βββ */ +.lb-row { + display: grid; + grid-template-columns: 36px 1fr auto; + gap: 10px; + align-items: center; + padding: 7px 0; + border-bottom: 1px dashed rgba(255,255,255,0.04); + font-size: 12px; +} +.lb-row:last-child { border-bottom: none; } +.lb-row.lb-row-self { + background: linear-gradient(90deg, rgba(201,162,39,0.08), transparent); + padding-left: 6px; + border-left: 2px solid var(--warning, #c9a227); +} + +.lb-rank { + font-family: var(--font-mono); + font-weight: 700; + color: var(--status-green, #00cc33); + font-size: 11px; + letter-spacing: 1px; +} +.lb-rank-1 { color: #ffd700; text-shadow: 0 0 8px rgba(255,215,0,0.4); } +.lb-rank-2 { color: #c0c0c0; } +.lb-rank-3 { color: #cd7f32; } + +.lb-label { + color: var(--text-primary, #e0e0e0); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 12px; +} +.lb-flag { margin-right: 6px; } +.lb-sub { + color: var(--text-secondary, #888); + font-size: 10px; + letter-spacing: 0.5px; + margin-left: 6px; +} + +.lb-count { + color: var(--accent-solana, #14F195); + font-weight: 600; + font-variant-numeric: tabular-nums; + font-size: 12px; +} + +.lb-bar-wrap { + grid-column: 1 / -1; + height: 3px; + background: rgba(255,255,255,0.04); + margin-top: 4px; + overflow: hidden; + border-radius: 1px; +} +.lb-bar { + height: 100%; + background: linear-gradient(90deg, var(--status-green, #00cc33), var(--accent-solana, #14F195)); + transform-origin: left center; + animation: lb-grow 0.7s cubic-bezier(0.22, 1, 0.36, 1) forwards; + width: 0%; + box-shadow: 0 0 8px rgba(0, 204, 51, 0.35); +} +@keyframes lb-grow { + from { width: 0%; } +} + +/* βββ Peak hours chart βββ */ +.lb-hours { + display: grid; + grid-template-columns: repeat(24, 1fr); + gap: 2px; + align-items: end; + height: 180px; + padding-bottom: 22px; + position: relative; +} +.lb-hour-col { + background: var(--status-green, #00cc33); + min-height: 2px; + border-radius: 1px 1px 0 0; + position: relative; + opacity: 0.75; + transition: opacity 0.15s, transform 0.15s; + box-shadow: 0 0 6px rgba(0, 204, 51, 0.25); +} +.lb-hour-col:hover { + opacity: 1; + transform: scaleY(1.03); + background: var(--accent-solana, #14F195); +} +.lb-hour-col::after { + content: attr(data-hour); + position: absolute; + bottom: -18px; + left: 50%; + transform: translateX(-50%); + font-size: 9px; + color: var(--text-secondary, #888); + font-family: var(--font-mono); +} +.lb-hour-col[data-showlabel="0"]::after { display: none; } +.lb-hour-col[data-peak="1"] { + background: var(--warning, #c9a227); + box-shadow: 0 0 12px rgba(201, 162, 39, 0.6); + opacity: 1; +} + +/* βββ Footer βββ */ +.lb-footer { + margin-top: 36px; + padding: 18px 0; + border-top: 1px solid var(--border-color, #2a2a2a); + font-family: var(--font-mono); + font-size: 10px; + color: var(--text-secondary, #777); + letter-spacing: 1px; + text-align: center; +} +.lb-footer code { + color: var(--status-green, #00cc33); + background: rgba(0, 204, 51, 0.08); + padding: 1px 6px; + border-radius: 2px; +} +.lb-footer p { margin: 3px 0; } + +/* βββ Mobile βββ */ +@media (max-width: 768px) { + .lb-grid { grid-template-columns: 1fr; gap: 14px; } + .lb-panel-body { max-height: 340px; min-height: 180px; } + .lb-hours { height: 140px; } + .lb-meta { font-size: 10px; gap: 8px; } + .lb-refresh-btn { margin-left: 0; } +} +@media (max-width: 420px) { + .lb-panel-header h2 { font-size: 11px; letter-spacing: 1px; } + .lb-panel-body { padding: 10px; } + .lb-row { grid-template-columns: 28px 1fr auto; font-size: 11px; } + .lb-label { font-size: 11px; } + .lb-hours { height: 110px; gap: 1px; } + .lb-hour-col::after { font-size: 8px; } +} diff --git a/hq/leaderboards.html b/hq/leaderboards.html new file mode 100644 index 0000000..d008ebe --- /dev/null +++ b/hq/leaderboards.html @@ -0,0 +1,116 @@ + + +
+ + +// LIVE OPERATIONAL RANKINGS β DERIVED FROM NGINX ACCESS LOGS
+ +' + escapeHtml(r.label) + '';
+ }
+ });
+ }
+
+ function renderReferrers(list) {
+ const rows = (list || []).slice(0, 10).map(r => ({
+ label: (r.referrer || 'direct'),
+ count: r.count,
+ }));
+ if (!rows.length) {
+ const panel = document.getElementById('panelReferrers');
+ const body = panel.querySelector('.lb-panel-body');
+ body.innerHTML = '' + String(peakIdx).padStart(2, '0') + ':00 UTC β ' + fmtNum(max) + ' req Β· Total: ' + fmtNum(counts.reduce((a,b)=>a+b,0)) + ' req/24h'
+ });
+ body.appendChild(summary);
+ }
+
+ function renderOperators(list) {
+ const panel = document.getElementById('panelOperators');
+ const body = panel.querySelector('.lb-panel-body');
+ body.innerHTML = '';
+ if (!list || !list.length) {
+ body.appendChild(el('div', { class: 'lb-loading', text: '(no data yet)' }));
+ return;
+ }
+ const max = Math.max.apply(null, list.map(o => o.count || 0)) || 1;
+ list.slice(0, 10).forEach((op, i) => {
+ const isSelf = visitorIpPrefix && op.ip_masked && op.ip_masked.startsWith(visitorIpPrefix + '.');
+ const row = el('div', { class: 'lb-row' + (isSelf ? ' lb-row-self' : '') });
+ row.appendChild(rank(i + 1));
+ const lbl = el('div', { class: 'lb-label' });
+ lbl.innerHTML = '' + escapeHtml(op.ip_masked) + '' +
+ (isSelf ? ' (YOU)' : '') +
+ ' Β· last seen ' + escapeHtml(op.last_seen || 'β') + '';
+ row.appendChild(lbl);
+ row.appendChild(el('span', { class: 'lb-count', text: fmtNum(op.count) + ' req' }));
+ const barWrap = el('div', { class: 'lb-bar-wrap' });
+ const bar = el('div', { class: 'lb-bar' });
+ if (isSelf) bar.style.background = 'linear-gradient(90deg, var(--warning, #c9a227), var(--status-green, #00cc33))';
+ barWrap.appendChild(bar);
+ row.appendChild(barWrap);
+ body.appendChild(row);
+ const pct = Math.max(2, Math.round((op.count / max) * 100));
+ requestAnimationFrame(() => { bar.style.width = pct + '%'; });
+ });
+ }
+
+ function escapeHtml(s) {
+ return String(s || '').replace(/[&<>"']/g, function (c) {
+ return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
+ });
+ }
+
+ // βββ Main fetch/update ββββββββββββββββββββββββββ
+ async function loadSelfIp() {
+ try {
+ const r = await fetch('/api/visitor/scan');
+ if (!r.ok) return;
+ const d = await r.json();
+ if (d.ip_masked) {
+ visitorIpPrefix = d.ip_masked.split('.')[0]; // first octet
+ }
+ } catch (e) { /* ignore */ }
+ }
+
+ async function fetchAndRender() {
+ try {
+ const r = await fetch(API_URL, { cache: 'no-store' });
+ if (!r.ok) throw new Error('HTTP ' + r.status);
+ const d = await r.json();
+ lastFetchedAt = new Date();
+
+ document.getElementById('lbReqs24h').textContent = '24h REQUESTS: ' + fmtNum(d.total_requests_24h);
+ document.getElementById('lbReqs7d').textContent = '7d REQUESTS: ' + fmtNum(d.total_requests_7d);
+
+ renderCountries(d.top_countries);
+ renderPages(d.top_pages);
+ renderReferrers(d.top_referrers);
+ renderPeakHours(d.peak_hours);
+ renderBrowsers(d.browsers);
+ renderOperators(d.top_operators);
+
+ updateUpdatedLabel();
+ } catch (e) {
+ console.error('leaderboards fetch failed:', e);
+ document.getElementById('lbUpdated').textContent = 'β FETCH ERROR β retrying...';
+ }
+ }
+
+ function updateUpdatedLabel() {
+ if (!lastFetchedAt) return;
+ const s = Math.floor((Date.now() - lastFetchedAt.getTime()) / 1000);
+ document.getElementById('lbUpdated').textContent =
+ 'β LAST UPDATED ' + (s < 5 ? 'just now' : s + 's ago');
+ }
+
+ // βββ Init ββββββββββββββββββββββββββββββββββββββ
+ document.addEventListener('DOMContentLoaded', async function () {
+ await loadSelfIp();
+ await fetchAndRender();
+ setInterval(fetchAndRender, REFRESH_MS);
+ setInterval(updateUpdatedLabel, 1000);
+ const btn = document.getElementById('lbRefresh');
+ if (btn) btn.addEventListener('click', function () {
+ btn.disabled = true; btn.textContent = 'β» REFRESHINGβ¦';
+ fetchAndRender().then(() => {
+ setTimeout(() => { btn.disabled = false; btn.textContent = 'β» REFRESH'; }, 400);
+ });
+ });
+ });
+})();