Update positioning, remove IPs from cards, remove icons, update NOC content

This commit is contained in:
2026-05-22 00:42:21 -05:00
parent 13141443c6
commit 295effeba1
3 changed files with 52 additions and 85 deletions
+25 -28
View File
@@ -1,6 +1,6 @@
# Ced's NOC Live Network Operations Center
# Ced's NOC Live Network Operations Center
> A custom-built, public-facing Network Operations Center dashboard running at [noc.chasedumphord.com](https://noc.chasedumphord.com) providing real-time visibility into Ced's HomeLab infrastructure, services, and RF edge systems.
> A custom-built, public facing Network Operations Center dashboard running at [noc.chasedumphord.com](https://noc.chasedumphord.com) providing real time visibility into Ced's HomeLab infrastructure, services, and RF edge systems.
[![Live NOC](https://img.shields.io/badge/Live-noc.chasedumphord.com-1D9E75?style=flat-square)](https://noc.chasedumphord.com)
[![Portfolio](https://img.shields.io/badge/Portfolio-chasedumphord.com-0F6E56?style=flat-square)](https://chasedumphord.com)
@@ -11,11 +11,9 @@
## What This Is
This is the public-facing front door to Ced's HomeLab infrastructure. This is the frontend layer of Ced's observability platform a custom-built status page that makes live infrastructure data publicly
accessible without requiring login. The backend engine ([ceds-observability-stack](https://github.com/ced4568/ceds-observability-stack)) handles metrics collection via Prometheus and Grafana. This repo is
what the public sees — real-time service status, latency, and health across every layer of the homelab. no login required.
This is the public-facing front door to Ced's HomeLab infrastructure. This is the frontend layer of Ced's observability platform a custom built status page that makes live infrastructure data publicly accessible without requiring login. The backend engine ([ceds-observability-stack](https://github.com/ced4568/ceds-observability-stack)) handles metrics collection via Prometheus and Grafana. This repo is what the public sees real-time service status, latency, and health across every layer of the homelab. No login required.
It's not a template or a third-party uptime service. It's a purpose-built frontend that pulls live status data and presents it as a professional NOC dashboard. Every card on the page represents a real, running service with real latency and uptime data.
It's not a template or a third party uptime service. It's a purpose built frontend that pulls live status data and presents it as a professional NOC dashboard. Every card on the page represents a real, running service with real latency and uptime data.
---
@@ -23,7 +21,7 @@ It's not a template or a third-party uptime service. It's a purpose-built fronte
[![NOC Systems](./screenshots/noc-systems.png)](https://noc.chasedumphord.com)
The NOC displays real-time status for every layer of the infrastructure stack from the Proxmox hypervisor down to the APRS RF edge system.
The NOC displays real time status for every layer of the infrastructure stack from the Proxmox hypervisor down to the APRS RF edge system.
---
@@ -49,8 +47,8 @@ The NOC displays real-time status for every layer of the infrastructure stack
| Service | Role | Status Source |
|---------|------|--------------|
| APRS Home iGate | RF-to-internet gateway (KJ5JCO-10) | TCP probe via Blackbox |
| APRS-IS | External APRS internet service | TCP probe noam.aprs2.net:14580 |
| APRS Home iGate | RF to internet gateway (KJ5JCO-10) | TCP probe via Blackbox |
| APRS-IS | External APRS internet service | TCP probe noam.aprs2.net:14580 |
---
@@ -89,7 +87,7 @@ graph LR
2. Prometheus collects and stores probe results
3. Python scripts generate status JSON from Prometheus data
4. NOC frontend reads JSON and renders live status cards
5. Public visitors see real-time infrastructure status no login needed
5. Public visitors see real time infrastructure status no login needed
---
@@ -119,21 +117,20 @@ ceds-noc/
Each service card on the NOC displays:
- **Service name and role** what it is and what it does
- **Category badge** HYPERVISOR, STORAGE, CLUSTER, PORTAL, etc.
- **Health badge** HEALTHY or DEGRADED based on probe results
- **Internal IP and port** — where the service lives on the HomeLab VLAN
- **Status** — Online / Offline
- **Latency** — real response time in milliseconds
- **Last check timestamp** — when the probe last ran
- **Service name and role** what it is and what it does
- **Category badge** HYPERVISOR, STORAGE, CLUSTER, PORTAL, etc.
- **Health badge** HEALTHY or DEGRADED based on probe results
- **Status** Online / Offline
- **Latency** real response time in milliseconds
- **Last check timestamp** when the probe last ran
This gives anyone viewing the page the same operational picture that the internal Grafana dashboard provides without requiring access to the internal network.
This gives anyone viewing the page the same operational picture that the internal Grafana dashboard provides without requiring access to the internal network.
---
## Screenshots
### Systems Overview Live Service Status
### Systems Overview Live Service Status
![NOC Systems](./screenshots/noc-systems.png)
### Metrics Overview
@@ -145,10 +142,10 @@ This gives anyone viewing the page the same operational picture that the interna
| Project | Role |
|---------|------|
| [ceds-observability-stack](https://github.com/ced4568/ceds-observability-stack) | Backend Prometheus, Grafana, Alertmanager, all exporters |
| [ceds-homelab](https://github.com/ced4568/ceds-homelab) | Infrastructure Proxmox cluster, TrueNAS, networking |
| [ced-k3s-homelab](https://github.com/ced4568/ced-k3s-homelab) | Orchestration 12-node Raspberry Pi K3s cluster |
| [ceds-aprs-igate](https://github.com/ced4568/ceds-aprs-igate) | Edge Dual-node APRS RF-to-internet gateway |
| [ceds-observability-stack](https://github.com/ced4568/ceds-observability-stack) | Backend Prometheus, Grafana, Alertmanager, all exporters |
| [ceds-homelab](https://github.com/ced4568/ceds-homelab) | Infrastructure Proxmox cluster, TrueNAS, networking |
| [ced-k3s-homelab](https://github.com/ced4568/ced-k3s-homelab) | Orchestration 12 node Raspberry Pi K3s cluster |
| [ceds-aprs-igate](https://github.com/ced4568/ceds-aprs-igate) | Edge Dual node APRS RF-to-internet gateway |
---
@@ -156,11 +153,11 @@ This gives anyone viewing the page the same operational picture that the interna
- [x] Live service status cards with real probe data
- [x] APRS RF edge system monitoring
- [x] Public access no login required
- [x] Public access no login required
- [x] Custom badge and status JSON generation
- [ ] Alerting integration visual alerts on NOC page
- [ ] Alerting integration visual alerts on NOC page
- [ ] Historical uptime percentage per service
- [ ] Mobile-optimized layout
- [ ] Mobile optimized layout
- [ ] Auto-refresh with configurable interval
- [ ] Public Grafana dashboard embed
@@ -169,9 +166,9 @@ This gives anyone viewing the page the same operational picture that the interna
## Author
**Chase Dumphord (Ced)**
Digital Systems Engineer · GE Aerospace · Oxford, MS
DevOps and Cloud Infrastructure Engineer · GE Aerospace · Oxford, MS
[![Portfolio](https://img.shields.io/badge/Portfolio-chasedumphord.com-0F6E56?style=flat-square)](https://chasedumphord.com)
[![LinkedIn](https://img.shields.io/badge/LinkedIn-chase--dumphord-0A66C2?style=flat-square)](https://www.linkedin.com/in/chase-dumphord/)
[![GitHub](https://img.shields.io/badge/GitHub-ced4568-181717?style=flat-square)](https://github.com/ced4568)
[![Live NOC](https://img.shields.io/badge/Live%20NOC-noc.chasedumphord.com-1D9E75?style=flat-square)](https://noc.chasedumphord.com)
[![Live NOC](https://img.shields.io/badge/Live%20NOC-noc.chasedumphord.com-1D9E75?style=flat-square)](https://noc.chasedumphord.com)
+16 -23
View File
@@ -17,12 +17,12 @@
<meta
name="description"
content="Ced's NOC — live network operations center dashboard showing real-time infrastructure health, service status, and edge system monitoring."
content="Ced's NOC — live network operations center dashboard showing real-time infrastructure health across 18 nodes and 28 services. Proxmox, K3s, TrueNAS, APRS edge systems."
/>
<!-- Open Graph -->
<meta property="og:title" content="Ced's NOC | Live Systems Dashboard" />
<meta property="og:description" content="Live infrastructure visibility — Proxmox, K3s, TrueNAS, APRS edge systems." />
<meta property="og:description" content="Live infrastructure visibility — 18 nodes, 28 services. Proxmox, K3s, TrueNAS, APRS edge systems. Always running." />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://noc.chasedumphord.com" />
@@ -36,7 +36,7 @@
<meta name="robots" content="index, follow" />
<meta name="author" content="Chase Dumphord" />
<!-- Fonts same as main site -->
<!-- Fonts same as main site -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Syne:wght@700;800&family=DM+Sans:wght@400;500;700&display=swap" rel="stylesheet" />
@@ -46,7 +46,6 @@
<!-- NOC-specific styles -->
<style>
/* ── NOC page overrides & extras ── */
.noc-page-hero {
padding: 3.5rem 0 2rem;
border-bottom: 1px solid var(--line);
@@ -90,12 +89,10 @@
.noc-page-meta a { color: var(--muted); }
.noc-page-meta a:hover { color: var(--accent-2); }
/* Full-width NOC content area */
.noc-full {
padding: 2.5rem 0 4rem;
}
/* Uptime summary bar */
.uptime-bar {
display: flex;
flex-wrap: wrap;
@@ -131,7 +128,6 @@
background: rgba(255, 95, 86, 0.06);
}
/* Layer filter tabs */
.layer-tabs {
display: flex;
flex-wrap: wrap;
@@ -159,14 +155,12 @@
background: rgba(94, 250, 204, 0.07);
}
/* Roadmap section on NOC page */
.noc-roadmap {
margin-top: 3rem;
padding-top: 2.5rem;
border-top: 1px solid var(--line);
}
/* Stack info footer on NOC page */
.noc-stack-bar {
margin-top: 2.5rem;
padding: 1rem 1.25rem;
@@ -195,7 +189,6 @@
text-decoration: underline;
}
/* Grafana CTA button row */
.noc-cta-row {
display: flex;
gap: 0.75rem;
@@ -213,7 +206,7 @@
<body>
<!-- ═══════════════════════════════ NAV ═══════════════════════════════ -->
<!-- NAV -->
<header class="site-header">
<div class="container nav">
<a href="https://chasedumphord.com" class="logo">Ced<span class="logo-cursor">_</span></a>
@@ -233,7 +226,7 @@
<main id="top">
<!-- ═══════════════════════════════ NOC HERO ═══════════════════════════════ -->
<!-- NOC HERO -->
<div class="noc-page-hero">
<div class="container">
<p class="eyebrow">
@@ -246,9 +239,9 @@
</h1>
<p class="noc-page-lead">
Real-time visibility into Ced's HomeLab infrastructure — Proxmox, K3s,
TrueNAS, APRS RF edge systems, and self-hosted services.
Not simulated. Not a demo. Running hardware.
Real-time visibility into Ced's HomeLab infrastructure across 18 nodes and 28 services
Proxmox, K3s, TrueNAS, APRS RF edge systems, and self-hosted services.
Built from the ground up and running in production every day. The infrastructure on this page is live. Not a demo.
</p>
<!-- Grafana CTA buttons -->
@@ -256,7 +249,7 @@
<a href="https://grafana.cedshomelab.com/dashboard/snapshot/PmEdg0ALfAVsmKN1Qgv7K11XP8h52ne5"
target="_blank" rel="noopener noreferrer"
class="btn btn-primary">
📊 View Live Grafana Dashboard
View Live Grafana Dashboard
</a>
<a href="https://github.com/ced4568/ceds-observability-stack"
target="_blank" rel="noopener noreferrer"
@@ -273,7 +266,7 @@
</div>
</div>
<!-- ═══════════════════════════════ NOC DASHBOARD ═══════════════════════════════ -->
<!-- NOC DASHBOARD -->
<div class="noc-full">
<div class="container">
@@ -333,7 +326,7 @@
<!-- Fallback -->
<div id="noc-fallback" class="noc-fallback" style="display:none;">
<p class="noc-fallback-msg">
📡 NOC feed unavailable from this context systems run locally.<br/>
NOC feed unavailable from this context systems run locally.<br/>
Data updates when the local pipeline pushes a fresh noc-status.json.
</p>
</div>
@@ -341,9 +334,9 @@
<!-- Stack info bar -->
<div class="noc-stack-bar">
<span><strong>Stack:</strong> Python health checks → JSON → JS dashboard</span>
<span><strong>Live:</strong> Grafana + Prometheus
<span><strong>Live:</strong> Grafana + Prometheus
<a href="https://grafana.cedshomelab.com/dashboard/snapshot/PmEdg0ALfAVsmKN1Qgv7K11XP8h52ne5"
target="_blank" rel="noopener noreferrer">View Dashboard</a>
target="_blank" rel="noopener noreferrer">View Dashboard</a>
</span>
<span><strong>Source:</strong>
<a href="https://github.com/ced4568/ceds-homelab" target="_blank" rel="noopener noreferrer">
@@ -379,7 +372,7 @@
<h3>Grafana + Prometheus</h3>
<p>
Full observability stack for Proxmox, TrueNAS, K3s, Raspberry Pi,
Uptime Kuma, and APRS edge systems with real-time metrics ingestion.
Uptime Kuma, and APRS edge systems with real time metrics ingestion.
Public dashboard available.
</p>
</div>
@@ -391,7 +384,7 @@
</main>
<!-- ═══════════════════════════════ FOOTER ═══════════════════════════════ -->
<!-- FOOTER -->
<footer class="site-footer">
<div class="container footer-content">
<p>© <span data-year></span> Chase Dumphord — <span class="footer-mono">noc.chasedumphord.com</span></p>
@@ -402,7 +395,7 @@
<!-- Shared script from main portfolio -->
<script src="https://chasedumphord.com/script.js"></script>
<!-- NOC page extras layer filter tabs + uptime pills -->
<!-- NOC page extras layer filter tabs + uptime pills -->
<script>
let allSystems = [];
+11 -34
View File
@@ -1,5 +1,5 @@
// ═══════════════════════════════════════════════
// Ced's Portfolio — script.js
// Ced's NOC script.js
// NOC data loads from data/noc-status.json
// Refreshes every 30s. Graceful fallback on fail.
// ═══════════════════════════════════════════════
@@ -34,8 +34,6 @@ async function loadNocStatus() {
const fallback = document.getElementById("noc-fallback");
try {
// Cache-bust to always get fresh data
// Use absolute URL when on NOC subdomain, relative when on main portfolio
const nocDataUrl = window.location.hostname === 'noc.chasedumphord.com'
? `https://chasedumphord.com/data/noc-status.json?t=${Date.now()}`
: `data/noc-status.json?t=${Date.now()}`;
@@ -46,32 +44,25 @@ async function loadNocStatus() {
const data = await res.json();
// Reset fail counter on success
failCount = 0;
// ── Status bar ──
if (dot) {
dot.className = "status-dot live";
}
if (dot) dot.className = "status-dot live";
if (statusText) {
const d = new Date(data.lastUpdated);
const formatted = d.toLocaleString('en-US', { month: 'long', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true });
statusText.textContent = `${data.nocName}${data.status} — Last updated: ${formatted}`;
}
if (badge) {
badge.textContent = `Auto-refresh: 30s`;
}
// Hide fallback if it was showing
if (badge) badge.textContent = `Auto-refresh: 30s`;
if (fallback) fallback.style.display = "none";
// ── Metrics ──
if (nodes) animateMetric(nodes, data.summary.nodes);
if (services) animateMetric(services, data.summary.services);
if (vlans) animateMetric(vlans, data.summary.vlans);
if (edge) animateMetric(edge, data.summary.edgeSystems);
// ── System cards ──
if (grid) {
grid.innerHTML = "";
data.systems.forEach((system) => {
@@ -95,7 +86,6 @@ async function loadNocStatus() {
</div>
<h3>${system.name}</h3>
<p>${system.type}</p>
<code>${system.address}</code>
<div class="system-meta">
<small>Status: ${system.status}</small>
<small>Latency: ${system.latency || "N/A"}</small>
@@ -107,14 +97,12 @@ async function loadNocStatus() {
});
}
// ── Alerts ──
if (alertList) renderAlerts(data.systems, alertList);
} catch (err) {
failCount++;
console.warn(`[NOC] Fetch failed (${failCount}/${MAX_FAILS}):`, err.message);
// Update dot to error state
if (dot) dot.className = "status-dot error";
if (statusText) {
@@ -125,27 +113,23 @@ async function loadNocStatus() {
if (badge) badge.textContent = "";
// After MAX_FAILS attempts, show the fallback block
if (failCount >= MAX_FAILS && fallback) {
fallback.style.display = "block";
}
}
}
// Animate a number metric with a quick pop
function animateMetric(el, value) {
el.classList.remove("metric-pop");
el.textContent = value;
void el.offsetWidth; // force reflow
void el.offsetWidth;
el.classList.add("metric-pop");
}
// ═══════════════════════════════════════════════
// GitHub Repos — fetches public repos for ced4568
// Sorted by last pushed, capped at 6 cards
// GitHub Repos
// ═══════════════════════════════════════════════
// Language → color map (subset of GitHub's palette)
const LANG_COLORS = {
Python: "#3572A5",
JavaScript: "#f1e05a",
@@ -170,8 +154,6 @@ async function loadGithubRepos() {
if (!res.ok) throw new Error(`GitHub API ${res.status}`);
const repos = await res.json();
// Filter out forks for cleaner display
const ownRepos = repos.filter((r) => !r.fork).slice(0, 6);
if (ownRepos.length === 0) {
@@ -181,10 +163,7 @@ async function loadGithubRepos() {
container.innerHTML = ownRepos.map((repo) => {
const langColor = LANG_COLORS[repo.language] || "var(--accent-2)";
const desc = repo.description
? repo.description
: "No description provided.";
const desc = repo.description ? repo.description : "No description provided.";
const updatedDate = new Date(repo.pushed_at).toLocaleDateString("en-US", {
month: "short",
year: "numeric",
@@ -203,9 +182,7 @@ async function loadGithubRepos() {
>${repo.name}</a>
<span class="repo-visibility">${repo.private ? "private" : "public"}</span>
</div>
<p class="repo-desc">${desc}</p>
<div class="repo-meta">
${repo.language
? `<span>
@@ -226,9 +203,9 @@ async function loadGithubRepos() {
container.innerHTML = `
<div class="repo-loading">
<p class="noc-loading">
Could not load repositories.
Could not load repositories.
<a href="https://github.com/ced4568" target="_blank" rel="noopener noreferrer">
View on GitHub
View on GitHub
</a>
</p>
</div>
@@ -236,7 +213,7 @@ async function loadGithubRepos() {
}
}
}
// Build the alert panel from system data
function renderAlerts(systems, alertList) {
const scored = systems
.map((sys) => {