mirror of
https://github.com/ced4568/ceds-noc.git
synced 2026-08-12 21:04:02 +00:00
Update positioning, remove IPs from cards, remove icons, update NOC content
This commit is contained in:
@@ -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.
|
||||||
|
|
||||||
[](https://noc.chasedumphord.com)
|
[](https://noc.chasedumphord.com)
|
||||||
[](https://chasedumphord.com)
|
[](https://chasedumphord.com)
|
||||||
@@ -11,11 +11,9 @@
|
|||||||
|
|
||||||
## What This Is
|
## 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
|
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.
|
||||||
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
|
|||||||
|
|
||||||
[](https://noc.chasedumphord.com)
|
[](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 |
|
| Service | Role | Status Source |
|
||||||
|---------|------|--------------|
|
|---------|------|--------------|
|
||||||
| APRS Home iGate | RF-to-internet gateway (KJ5JCO-10) | TCP probe via Blackbox |
|
| 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-IS | External APRS internet service | TCP probe noam.aprs2.net:14580 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -89,7 +87,7 @@ graph LR
|
|||||||
2. Prometheus collects and stores probe results
|
2. Prometheus collects and stores probe results
|
||||||
3. Python scripts generate status JSON from Prometheus data
|
3. Python scripts generate status JSON from Prometheus data
|
||||||
4. NOC frontend reads JSON and renders live status cards
|
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:
|
Each service card on the NOC displays:
|
||||||
|
|
||||||
- **Service name and role** — what it is and what it does
|
- **Service name and role** what it is and what it does
|
||||||
- **Category badge** — HYPERVISOR, STORAGE, CLUSTER, PORTAL, etc.
|
- **Category badge** HYPERVISOR, STORAGE, CLUSTER, PORTAL, etc.
|
||||||
- **Health badge** — HEALTHY or DEGRADED based on probe results
|
- **Health badge** HEALTHY or DEGRADED based on probe results
|
||||||
- **Internal IP and port** — where the service lives on the HomeLab VLAN
|
- **Status** Online / Offline
|
||||||
- **Status** — Online / Offline
|
- **Latency** real response time in milliseconds
|
||||||
- **Latency** — real response time in milliseconds
|
- **Last check timestamp** when the probe last ran
|
||||||
- **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
|
## Screenshots
|
||||||
|
|
||||||
### Systems Overview — Live Service Status
|
### Systems Overview Live Service Status
|
||||||

|

|
||||||
|
|
||||||
### Metrics Overview
|
### Metrics Overview
|
||||||
@@ -145,10 +142,10 @@ This gives anyone viewing the page the same operational picture that the interna
|
|||||||
|
|
||||||
| Project | Role |
|
| Project | Role |
|
||||||
|---------|------|
|
|---------|------|
|
||||||
| [ceds-observability-stack](https://github.com/ced4568/ceds-observability-stack) | Backend — Prometheus, Grafana, Alertmanager, all exporters |
|
| [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 |
|
| [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 |
|
| [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-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] Live service status cards with real probe data
|
||||||
- [x] APRS RF edge system monitoring
|
- [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
|
- [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
|
- [ ] Historical uptime percentage per service
|
||||||
- [ ] Mobile-optimized layout
|
- [ ] Mobile optimized layout
|
||||||
- [ ] Auto-refresh with configurable interval
|
- [ ] Auto-refresh with configurable interval
|
||||||
- [ ] Public Grafana dashboard embed
|
- [ ] Public Grafana dashboard embed
|
||||||
|
|
||||||
@@ -169,7 +166,7 @@ This gives anyone viewing the page the same operational picture that the interna
|
|||||||
## Author
|
## Author
|
||||||
|
|
||||||
**Chase Dumphord (Ced)**
|
**Chase Dumphord (Ced)**
|
||||||
Digital Systems Engineer · GE Aerospace · Oxford, MS
|
DevOps and Cloud Infrastructure Engineer · GE Aerospace · Oxford, MS
|
||||||
|
|
||||||
[](https://chasedumphord.com)
|
[](https://chasedumphord.com)
|
||||||
[](https://www.linkedin.com/in/chase-dumphord/)
|
[](https://www.linkedin.com/in/chase-dumphord/)
|
||||||
|
|||||||
+16
-23
@@ -17,12 +17,12 @@
|
|||||||
|
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
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 -->
|
<!-- Open Graph -->
|
||||||
<meta property="og:title" content="Ced's NOC | Live Systems Dashboard" />
|
<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:type" content="website" />
|
||||||
<meta property="og:url" content="https://noc.chasedumphord.com" />
|
<meta property="og:url" content="https://noc.chasedumphord.com" />
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<meta name="robots" content="index, follow" />
|
<meta name="robots" content="index, follow" />
|
||||||
<meta name="author" content="Chase Dumphord" />
|
<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.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<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" />
|
<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 -->
|
<!-- NOC-specific styles -->
|
||||||
<style>
|
<style>
|
||||||
/* ── NOC page overrides & extras ── */
|
|
||||||
.noc-page-hero {
|
.noc-page-hero {
|
||||||
padding: 3.5rem 0 2rem;
|
padding: 3.5rem 0 2rem;
|
||||||
border-bottom: 1px solid var(--line);
|
border-bottom: 1px solid var(--line);
|
||||||
@@ -90,12 +89,10 @@
|
|||||||
.noc-page-meta a { color: var(--muted); }
|
.noc-page-meta a { color: var(--muted); }
|
||||||
.noc-page-meta a:hover { color: var(--accent-2); }
|
.noc-page-meta a:hover { color: var(--accent-2); }
|
||||||
|
|
||||||
/* Full-width NOC content area */
|
|
||||||
.noc-full {
|
.noc-full {
|
||||||
padding: 2.5rem 0 4rem;
|
padding: 2.5rem 0 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Uptime summary bar */
|
|
||||||
.uptime-bar {
|
.uptime-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -131,7 +128,6 @@
|
|||||||
background: rgba(255, 95, 86, 0.06);
|
background: rgba(255, 95, 86, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Layer filter tabs */
|
|
||||||
.layer-tabs {
|
.layer-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -159,14 +155,12 @@
|
|||||||
background: rgba(94, 250, 204, 0.07);
|
background: rgba(94, 250, 204, 0.07);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Roadmap section on NOC page */
|
|
||||||
.noc-roadmap {
|
.noc-roadmap {
|
||||||
margin-top: 3rem;
|
margin-top: 3rem;
|
||||||
padding-top: 2.5rem;
|
padding-top: 2.5rem;
|
||||||
border-top: 1px solid var(--line);
|
border-top: 1px solid var(--line);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stack info footer on NOC page */
|
|
||||||
.noc-stack-bar {
|
.noc-stack-bar {
|
||||||
margin-top: 2.5rem;
|
margin-top: 2.5rem;
|
||||||
padding: 1rem 1.25rem;
|
padding: 1rem 1.25rem;
|
||||||
@@ -195,7 +189,6 @@
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Grafana CTA button row */
|
|
||||||
.noc-cta-row {
|
.noc-cta-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
@@ -213,7 +206,7 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<!-- ═══════════════════════════════ NAV ═══════════════════════════════ -->
|
<!-- NAV -->
|
||||||
<header class="site-header">
|
<header class="site-header">
|
||||||
<div class="container nav">
|
<div class="container nav">
|
||||||
<a href="https://chasedumphord.com" class="logo">Ced<span class="logo-cursor">_</span></a>
|
<a href="https://chasedumphord.com" class="logo">Ced<span class="logo-cursor">_</span></a>
|
||||||
@@ -233,7 +226,7 @@
|
|||||||
|
|
||||||
<main id="top">
|
<main id="top">
|
||||||
|
|
||||||
<!-- ═══════════════════════════════ NOC HERO ═══════════════════════════════ -->
|
<!-- NOC HERO -->
|
||||||
<div class="noc-page-hero">
|
<div class="noc-page-hero">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p class="eyebrow">
|
<p class="eyebrow">
|
||||||
@@ -246,9 +239,9 @@
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p class="noc-page-lead">
|
<p class="noc-page-lead">
|
||||||
Real-time visibility into Ced's HomeLab infrastructure — Proxmox, K3s,
|
Real-time visibility into Ced's HomeLab infrastructure across 18 nodes and 28 services
|
||||||
TrueNAS, APRS RF edge systems, and self-hosted services.
|
Proxmox, K3s, TrueNAS, APRS RF edge systems, and self-hosted services.
|
||||||
Not simulated. Not a demo. Running hardware.
|
Built from the ground up and running in production every day. The infrastructure on this page is live. Not a demo.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Grafana CTA buttons -->
|
<!-- Grafana CTA buttons -->
|
||||||
@@ -256,7 +249,7 @@
|
|||||||
<a href="https://grafana.cedshomelab.com/dashboard/snapshot/PmEdg0ALfAVsmKN1Qgv7K11XP8h52ne5"
|
<a href="https://grafana.cedshomelab.com/dashboard/snapshot/PmEdg0ALfAVsmKN1Qgv7K11XP8h52ne5"
|
||||||
target="_blank" rel="noopener noreferrer"
|
target="_blank" rel="noopener noreferrer"
|
||||||
class="btn btn-primary">
|
class="btn btn-primary">
|
||||||
📊 View Live Grafana Dashboard →
|
View Live Grafana Dashboard
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/ced4568/ceds-observability-stack"
|
<a href="https://github.com/ced4568/ceds-observability-stack"
|
||||||
target="_blank" rel="noopener noreferrer"
|
target="_blank" rel="noopener noreferrer"
|
||||||
@@ -273,7 +266,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ═══════════════════════════════ NOC DASHBOARD ═══════════════════════════════ -->
|
<!-- NOC DASHBOARD -->
|
||||||
<div class="noc-full">
|
<div class="noc-full">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
@@ -333,7 +326,7 @@
|
|||||||
<!-- Fallback -->
|
<!-- Fallback -->
|
||||||
<div id="noc-fallback" class="noc-fallback" style="display:none;">
|
<div id="noc-fallback" class="noc-fallback" style="display:none;">
|
||||||
<p class="noc-fallback-msg">
|
<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.
|
Data updates when the local pipeline pushes a fresh noc-status.json.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -341,9 +334,9 @@
|
|||||||
<!-- Stack info bar -->
|
<!-- Stack info bar -->
|
||||||
<div class="noc-stack-bar">
|
<div class="noc-stack-bar">
|
||||||
<span><strong>Stack:</strong> Python health checks → JSON → JS dashboard</span>
|
<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"
|
<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>
|
||||||
<span><strong>Source:</strong>
|
<span><strong>Source:</strong>
|
||||||
<a href="https://github.com/ced4568/ceds-homelab" target="_blank" rel="noopener noreferrer">
|
<a href="https://github.com/ced4568/ceds-homelab" target="_blank" rel="noopener noreferrer">
|
||||||
@@ -379,7 +372,7 @@
|
|||||||
<h3>Grafana + Prometheus</h3>
|
<h3>Grafana + Prometheus</h3>
|
||||||
<p>
|
<p>
|
||||||
Full observability stack for Proxmox, TrueNAS, K3s, Raspberry Pi,
|
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.
|
Public dashboard available.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -391,7 +384,7 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- ═══════════════════════════════ FOOTER ═══════════════════════════════ -->
|
<!-- FOOTER -->
|
||||||
<footer class="site-footer">
|
<footer class="site-footer">
|
||||||
<div class="container footer-content">
|
<div class="container footer-content">
|
||||||
<p>© <span data-year></span> Chase Dumphord — <span class="footer-mono">noc.chasedumphord.com</span></p>
|
<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 -->
|
<!-- Shared script from main portfolio -->
|
||||||
<script src="https://chasedumphord.com/script.js"></script>
|
<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>
|
<script>
|
||||||
let allSystems = [];
|
let allSystems = [];
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// ═══════════════════════════════════════════════
|
// ═══════════════════════════════════════════════
|
||||||
// Ced's Portfolio — script.js
|
// Ced's NOC script.js
|
||||||
// NOC data loads from data/noc-status.json
|
// NOC data loads from data/noc-status.json
|
||||||
// Refreshes every 30s. Graceful fallback on fail.
|
// Refreshes every 30s. Graceful fallback on fail.
|
||||||
// ═══════════════════════════════════════════════
|
// ═══════════════════════════════════════════════
|
||||||
@@ -34,8 +34,6 @@ async function loadNocStatus() {
|
|||||||
const fallback = document.getElementById("noc-fallback");
|
const fallback = document.getElementById("noc-fallback");
|
||||||
|
|
||||||
try {
|
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'
|
const nocDataUrl = window.location.hostname === 'noc.chasedumphord.com'
|
||||||
? `https://chasedumphord.com/data/noc-status.json?t=${Date.now()}`
|
? `https://chasedumphord.com/data/noc-status.json?t=${Date.now()}`
|
||||||
: `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();
|
const data = await res.json();
|
||||||
|
|
||||||
// Reset fail counter on success
|
|
||||||
failCount = 0;
|
failCount = 0;
|
||||||
|
|
||||||
// ── Status bar ──
|
if (dot) dot.className = "status-dot live";
|
||||||
if (dot) {
|
|
||||||
dot.className = "status-dot live";
|
|
||||||
}
|
|
||||||
if (statusText) {
|
if (statusText) {
|
||||||
const d = new Date(data.lastUpdated);
|
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 });
|
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}`;
|
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";
|
if (fallback) fallback.style.display = "none";
|
||||||
|
|
||||||
// ── Metrics ──
|
|
||||||
if (nodes) animateMetric(nodes, data.summary.nodes);
|
if (nodes) animateMetric(nodes, data.summary.nodes);
|
||||||
if (services) animateMetric(services, data.summary.services);
|
if (services) animateMetric(services, data.summary.services);
|
||||||
if (vlans) animateMetric(vlans, data.summary.vlans);
|
if (vlans) animateMetric(vlans, data.summary.vlans);
|
||||||
if (edge) animateMetric(edge, data.summary.edgeSystems);
|
if (edge) animateMetric(edge, data.summary.edgeSystems);
|
||||||
|
|
||||||
// ── System cards ──
|
|
||||||
if (grid) {
|
if (grid) {
|
||||||
grid.innerHTML = "";
|
grid.innerHTML = "";
|
||||||
data.systems.forEach((system) => {
|
data.systems.forEach((system) => {
|
||||||
@@ -95,7 +86,6 @@ async function loadNocStatus() {
|
|||||||
</div>
|
</div>
|
||||||
<h3>${system.name}</h3>
|
<h3>${system.name}</h3>
|
||||||
<p>${system.type}</p>
|
<p>${system.type}</p>
|
||||||
<code>${system.address}</code>
|
|
||||||
<div class="system-meta">
|
<div class="system-meta">
|
||||||
<small>Status: ${system.status}</small>
|
<small>Status: ${system.status}</small>
|
||||||
<small>Latency: ${system.latency || "N/A"}</small>
|
<small>Latency: ${system.latency || "N/A"}</small>
|
||||||
@@ -107,14 +97,12 @@ async function loadNocStatus() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Alerts ──
|
|
||||||
if (alertList) renderAlerts(data.systems, alertList);
|
if (alertList) renderAlerts(data.systems, alertList);
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
failCount++;
|
failCount++;
|
||||||
console.warn(`[NOC] Fetch failed (${failCount}/${MAX_FAILS}):`, err.message);
|
console.warn(`[NOC] Fetch failed (${failCount}/${MAX_FAILS}):`, err.message);
|
||||||
|
|
||||||
// Update dot to error state
|
|
||||||
if (dot) dot.className = "status-dot error";
|
if (dot) dot.className = "status-dot error";
|
||||||
|
|
||||||
if (statusText) {
|
if (statusText) {
|
||||||
@@ -125,27 +113,23 @@ async function loadNocStatus() {
|
|||||||
|
|
||||||
if (badge) badge.textContent = "";
|
if (badge) badge.textContent = "";
|
||||||
|
|
||||||
// After MAX_FAILS attempts, show the fallback block
|
|
||||||
if (failCount >= MAX_FAILS && fallback) {
|
if (failCount >= MAX_FAILS && fallback) {
|
||||||
fallback.style.display = "block";
|
fallback.style.display = "block";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animate a number metric with a quick pop
|
|
||||||
function animateMetric(el, value) {
|
function animateMetric(el, value) {
|
||||||
el.classList.remove("metric-pop");
|
el.classList.remove("metric-pop");
|
||||||
el.textContent = value;
|
el.textContent = value;
|
||||||
void el.offsetWidth; // force reflow
|
void el.offsetWidth;
|
||||||
el.classList.add("metric-pop");
|
el.classList.add("metric-pop");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════
|
// ═══════════════════════════════════════════════
|
||||||
// GitHub Repos — fetches public repos for ced4568
|
// GitHub Repos
|
||||||
// Sorted by last pushed, capped at 6 cards
|
|
||||||
// ═══════════════════════════════════════════════
|
// ═══════════════════════════════════════════════
|
||||||
|
|
||||||
// Language → color map (subset of GitHub's palette)
|
|
||||||
const LANG_COLORS = {
|
const LANG_COLORS = {
|
||||||
Python: "#3572A5",
|
Python: "#3572A5",
|
||||||
JavaScript: "#f1e05a",
|
JavaScript: "#f1e05a",
|
||||||
@@ -170,8 +154,6 @@ async function loadGithubRepos() {
|
|||||||
if (!res.ok) throw new Error(`GitHub API ${res.status}`);
|
if (!res.ok) throw new Error(`GitHub API ${res.status}`);
|
||||||
|
|
||||||
const repos = await res.json();
|
const repos = await res.json();
|
||||||
|
|
||||||
// Filter out forks for cleaner display
|
|
||||||
const ownRepos = repos.filter((r) => !r.fork).slice(0, 6);
|
const ownRepos = repos.filter((r) => !r.fork).slice(0, 6);
|
||||||
|
|
||||||
if (ownRepos.length === 0) {
|
if (ownRepos.length === 0) {
|
||||||
@@ -181,10 +163,7 @@ async function loadGithubRepos() {
|
|||||||
|
|
||||||
container.innerHTML = ownRepos.map((repo) => {
|
container.innerHTML = ownRepos.map((repo) => {
|
||||||
const langColor = LANG_COLORS[repo.language] || "var(--accent-2)";
|
const langColor = LANG_COLORS[repo.language] || "var(--accent-2)";
|
||||||
const desc = repo.description
|
const desc = repo.description ? repo.description : "No description provided.";
|
||||||
? repo.description
|
|
||||||
: "No description provided.";
|
|
||||||
|
|
||||||
const updatedDate = new Date(repo.pushed_at).toLocaleDateString("en-US", {
|
const updatedDate = new Date(repo.pushed_at).toLocaleDateString("en-US", {
|
||||||
month: "short",
|
month: "short",
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
@@ -203,9 +182,7 @@ async function loadGithubRepos() {
|
|||||||
>${repo.name}</a>
|
>${repo.name}</a>
|
||||||
<span class="repo-visibility">${repo.private ? "private" : "public"}</span>
|
<span class="repo-visibility">${repo.private ? "private" : "public"}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="repo-desc">${desc}</p>
|
<p class="repo-desc">${desc}</p>
|
||||||
|
|
||||||
<div class="repo-meta">
|
<div class="repo-meta">
|
||||||
${repo.language
|
${repo.language
|
||||||
? `<span>
|
? `<span>
|
||||||
@@ -228,7 +205,7 @@ async function loadGithubRepos() {
|
|||||||
<p class="noc-loading">
|
<p class="noc-loading">
|
||||||
Could not load repositories.
|
Could not load repositories.
|
||||||
<a href="https://github.com/ced4568" target="_blank" rel="noopener noreferrer">
|
<a href="https://github.com/ced4568" target="_blank" rel="noopener noreferrer">
|
||||||
View on GitHub →
|
View on GitHub
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -236,7 +213,7 @@ async function loadGithubRepos() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Build the alert panel from system data
|
|
||||||
function renderAlerts(systems, alertList) {
|
function renderAlerts(systems, alertList) {
|
||||||
const scored = systems
|
const scored = systems
|
||||||
.map((sys) => {
|
.map((sys) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user