From 271f933b6eb17e07f9a4e44508ef66fdd26eadb0 Mon Sep 17 00:00:00 2001 From: jae Date: Tue, 31 Mar 2026 21:14:30 +0000 Subject: [PATCH] feat: wire homepage to live API (blog feed, server stats, weather, now playing) --- css/style.css | 32 ++++++++++ index.html | 46 +++------------ js/main.js | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+), 37 deletions(-) diff --git a/css/style.css b/css/style.css index eebdeb0..5e00f6c 100644 --- a/css/style.css +++ b/css/style.css @@ -1600,3 +1600,35 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); } .metric-row { grid-template-columns: 60px 1fr 35px; gap: 0.5rem; } .graph-stats { flex-direction: column; gap: 0.5rem; } } + +/* ─── Blog Loading Placeholder ─── */ +.blog-loading-placeholder { + grid-column: 1 / -1; + display: flex; + align-items: center; + justify-content: center; + gap: 1rem; + padding: 3rem; + font-family: 'JetBrains Mono', monospace; + font-size: 0.8rem; + color: rgba(0, 255, 200, 0.4); + letter-spacing: 2px; +} + +.loading-spinner { + width: 16px; + height: 16px; + border: 2px solid rgba(0, 255, 200, 0.1); + border-top-color: #00ffc8; + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +.blog-view-all-link:hover { + background: rgba(0, 255, 200, 0.08) !important; + box-shadow: 0 0 15px rgba(0, 255, 200, 0.15); +} diff --git a/index.html b/index.html index 0e69d6a..29ca372 100644 --- a/index.html +++ b/index.html @@ -349,43 +349,15 @@

BLOG_FEED

-
-
-
- 2026.03.31 - INFRASTRUCTURE -
-

Self-Hosting Everything: A Complete Guide

-

How I migrated away from cloud services and built a fully self-hosted infrastructure stack...

- -
-
-
- 2026.03.28 - SECURITY -
-

Hardening Your VPS: Beyond the Basics

-

Essential security configurations that most tutorials skip — from kernel parameters to network isolation...

- -
-
-
- 2026.03.22 - DEV -
-

Building AI Agents That Actually Work

-

My journey deploying Agent Zero and customising autonomous AI assistants for real-world tasks...

- -
+
+ +
+ + LOADING TRANSMISSIONS... +
+
+
diff --git a/js/main.js b/js/main.js index d4b6300..949f5ff 100644 --- a/js/main.js +++ b/js/main.js @@ -741,6 +741,158 @@ }, 5000); } + // ─── API CONFIG ─── + const API_BASE = window.location.hostname === 'localhost' ? 'http://localhost:5000' : '/api'; + + // ─── BLOG FEED (dynamic from API) ─── + function initBlogFeed() { + const grid = document.getElementById('blogGrid'); + if (!grid) return; + + fetch(API_BASE + '/posts') + .then(r => r.json()) + .then(posts => { + posts.sort((a, b) => new Date(b.date) - new Date(a.date)); + const latest = posts.slice(0, 3); + + if (latest.length === 0) { + grid.innerHTML = '
NO TRANSMISSIONS FOUND
'; + return; + } + + grid.innerHTML = latest.map((post, i) => ` +
+
+ ${post.date.replace(/-/g, '.')} + ${(post.tags && post.tags[0]) ? post.tags[0].toUpperCase() : 'POST'} +
+

${post.title}

+

${post.excerpt || (post.content || '').substring(0, 120) + '...'}

+ +
+ `).join(''); + + // Animate cards in + requestAnimationFrame(() => { + grid.querySelectorAll('.blog-card').forEach(card => { + card.style.opacity = '1'; + card.style.transform = 'translateY(0)'; + }); + }); + }) + .catch(() => { + grid.innerHTML = '
SIGNAL LOST — RETRY LATER
'; + }); + } + + // ─── LIVE SERVER STATS (from API) ─── + function initLiveStats() { + function fetchStats() { + fetch(API_BASE + '/stats') + .then(r => r.json()) + .then(d => { + // Update metric bars with real data + const metrics = [ + { bar: 'cpuBar', val: 'cpuVal', value: d.cpu_percent }, + { bar: 'memBar', val: 'memVal', value: d.memory_percent }, + { bar: 'diskBar', val: 'diskVal', value: d.disk_percent }, + ]; + metrics.forEach(m => { + const barEl = document.getElementById(m.bar); + const valEl = document.getElementById(m.val); + if (!barEl || !valEl) return; + barEl.style.width = m.value + '%'; + valEl.textContent = Math.round(m.value) + '%'; + if (m.value > 85) { + barEl.style.background = 'linear-gradient(90deg, #ff4757, rgba(255,71,87,0.4))'; + barEl.style.boxShadow = '0 0 8px rgba(255,71,87,0.4)'; + valEl.style.color = '#ff4757'; + } else if (m.value > 70) { + barEl.style.background = 'linear-gradient(90deg, #ffa502, rgba(255,165,2,0.4))'; + barEl.style.boxShadow = '0 0 8px rgba(255,165,2,0.3)'; + valEl.style.color = '#ffa502'; + } else { + barEl.style.background = ''; + barEl.style.boxShadow = ''; + valEl.style.color = ''; + } + }); + + // Update uptime from real data + const uptimeEl = document.getElementById('uptime'); + if (uptimeEl && d.uptime_seconds) { + const totalSec = Math.floor(d.uptime_seconds); + const days = Math.floor(totalSec / 86400); + const hours = Math.floor((totalSec % 86400) / 3600); + const mins = Math.floor((totalSec % 3600) / 60); + uptimeEl.textContent = days + 'd ' + String(hours).padStart(2, '0') + 'h ' + String(mins).padStart(2, '0') + 'm'; + } + + // Update server health + const healthEl = document.getElementById('serverHealth'); + if (healthEl) { + const health = Math.round(100 - (d.cpu_percent * 0.3 + d.memory_percent * 0.3 + d.disk_percent * 0.4) / 3); + healthEl.textContent = Math.min(99, Math.max(80, health)) + '%'; + } + + // Container counts + const containerEl = document.getElementById('containerUp'); + const containerTotalEl = document.querySelector('.container-total'); + if (containerEl) containerEl.textContent = d.container_running; + if (containerTotalEl) containerTotalEl.textContent = d.container_total; + }) + .catch(() => {}); + } + + fetchStats(); + setInterval(fetchStats, 10000); // refresh every 10s + } + + // ─── LIVE WEATHER ─── + function initLiveWeather() { + const tempEl = document.getElementById('weatherTemp'); + const condEl = document.getElementById('weatherCond'); + if (!tempEl && !condEl) return; + + fetch(API_BASE + '/weather') + .then(r => r.json()) + .then(d => { + if (tempEl) tempEl.textContent = d.temp_c + '°C'; + if (condEl) condEl.textContent = d.condition; + const feelsEl = document.getElementById('weatherFeels'); + if (feelsEl) feelsEl.textContent = 'FEELS ' + d.feels_like + '°C'; + const windEl = document.getElementById('weatherWind'); + if (windEl) windEl.textContent = d.wind_kph + ' KPH ' + d.wind_dir; + }) + .catch(() => {}); + } + + // ─── NOW PLAYING ─── + function initNowPlaying() { + const trackEl = document.getElementById('npTrack'); + const artistEl = document.getElementById('npArtist'); + if (!trackEl && !artistEl) return; + + function fetchTrack() { + fetch(API_BASE + '/nowplaying') + .then(r => r.json()) + .then(d => { + if (trackEl) trackEl.textContent = d.track; + if (artistEl) artistEl.textContent = d.artist; + const albumEl = document.getElementById('npAlbum'); + if (albumEl) albumEl.textContent = d.album; + }) + .catch(() => {}); + } + + fetchTrack(); + setInterval(fetchTrack, 30000); + } + + document.addEventListener('DOMContentLoaded', () => { initNavbar(); initClock(); @@ -761,6 +913,12 @@ initPowerFlicker(); initServerHealth(); + // Live API integrations + initBlogFeed(); + initLiveStats(); + initLiveWeather(); + initNowPlaying(); + // Page load animation document.body.style.opacity = '0';