Update noc-status.json fetch to absolute URL

This commit is contained in:
2026-05-02 17:40:10 -05:00
parent ad4920b61a
commit cc1a4bac16
+232 -109
View File
@@ -1,148 +1,271 @@
document.addEventListener("DOMContentLoaded", () => { // ═══════════════════════════════════════════════
const yearSpan = document.querySelector("[data-year]"); // Ced's Portfolio — script.js
if (yearSpan) { // NOC data loads from data/noc-status.json
yearSpan.textContent = new Date().getFullYear(); // Refreshes every 30s. Graceful fallback on fail.
} // ═══════════════════════════════════════════════
document.addEventListener("DOMContentLoaded", () => {
// Set copyright year
const yearSpan = document.querySelector("[data-year]");
if (yearSpan) yearSpan.textContent = new Date().getFullYear();
// NOC data
loadNocStatus(); loadNocStatus();
setInterval(loadNocStatus, 30000); setInterval(loadNocStatus, 30000);
// GitHub pinned repos
loadGithubRepos();
}); });
async function loadNocStatus() { // Track consecutive failures so we can show the fallback
try { let failCount = 0;
const response = await fetch("data/noc-status.json"); const MAX_FAILS = 3;
if (!response.ok) { async function loadNocStatus() {
throw new Error(`HTTP error: ${response.status}`); const dot = document.getElementById("noc-dot");
const statusText = document.getElementById("noc-status-text");
const badge = document.getElementById("noc-refresh-badge");
const nodes = document.getElementById("metric-nodes");
const services = document.getElementById("metric-services");
const vlans = document.getElementById("metric-vlans");
const edge = document.getElementById("metric-edge");
const grid = document.getElementById("noc-system-grid");
const alertList = document.getElementById("noc-alert-list");
const fallback = document.getElementById("noc-fallback");
try {
// Cache-bust to always get fresh data
const res = await fetch(`https://chasedumphord.com/data/noc-status.json?t=${Date.now()}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
// Reset fail counter on success
failCount = 0;
// ── Status bar ──
if (dot) {
dot.className = "status-dot live";
}
if (statusText) {
statusText.textContent = `${data.nocName}${data.status} — Last updated: ${data.lastUpdated}`;
}
if (badge) {
badge.textContent = `Auto-refresh: 30s`;
} }
const data = await response.json(); // Hide fallback if it was showing
if (fallback) fallback.style.display = "none";
const statusText = document.getElementById("noc-status-text"); // ── Metrics ──
const nodes = document.getElementById("metric-nodes"); if (nodes) animateMetric(nodes, data.summary.nodes);
const services = document.getElementById("metric-services"); if (services) animateMetric(services, data.summary.services);
const vlans = document.getElementById("metric-vlans"); if (vlans) animateMetric(vlans, data.summary.vlans);
const edge = document.getElementById("metric-edge"); if (edge) animateMetric(edge, data.summary.edgeSystems);
const grid = document.getElementById("noc-system-grid");
const alertList = document.getElementById("noc-alert-list");
if (!statusText || !nodes || !services || !vlans || !edge || !grid) { // ── System cards ──
if (grid) {
grid.innerHTML = "";
data.systems.forEach((system) => {
const card = document.createElement("article");
card.className = "noc-system-card";
const rawStatus = system.status.toLowerCase();
const statusClass =
rawStatus.includes("online") || rawStatus.includes("operational") ? "online"
: rawStatus.includes("building") || rawStatus.includes("tuning") || rawStatus.includes("active") ? "warning"
: "offline";
card.innerHTML = `
<div class="noc-system-top">
<span class="mini-status ${statusClass}"></span>
<span>${system.layer}</span>
</div>
<div class="system-label">${system.label || "System"}</div>
<div class="severity-pill ${(system.severity || "unknown").toLowerCase()}">
${system.severity || "Unknown"}
</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>
<small>Last Check: ${system.lastCheck || "Manual"}</small>
</div>
`;
grid.appendChild(card);
});
}
// ── 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) {
statusText.textContent = failCount >= MAX_FAILS
? "NOC feed offline — view live dashboard at noc.chasedumphord.com"
: `NOC data unavailable — retrying... (${failCount}/${MAX_FAILS})`;
}
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
el.classList.add("metric-pop");
}
// ═══════════════════════════════════════════════
// GitHub Repos — fetches public repos for ced4568
// Sorted by last pushed, capped at 6 cards
// ═══════════════════════════════════════════════
// Language → color map (subset of GitHub's palette)
const LANG_COLORS = {
Python: "#3572A5",
JavaScript: "#f1e05a",
Shell: "#89e051",
HTML: "#e34c26",
CSS: "#563d7c",
Dockerfile: "#384d54",
YAML: "#cb171e",
Makefile: "#427819",
};
async function loadGithubRepos() {
const container = document.getElementById("github-repos");
if (!container) return;
try {
const res = await fetch(
"https://api.github.com/users/ced4568/repos?sort=pushed&per_page=6",
{ headers: { Accept: "application/vnd.github+json" } }
);
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) {
container.innerHTML = `<p class="noc-loading">No public repositories found.</p>`;
return; return;
} }
statusText.textContent = `${data.nocName}${data.status} — Last updated: ${data.lastUpdated}`; container.innerHTML = ownRepos.map((repo) => {
const langColor = LANG_COLORS[repo.language] || "var(--accent-2)";
const desc = repo.description
? repo.description
: "No description provided.";
animateMetric(nodes, data.summary.nodes); const updatedDate = new Date(repo.pushed_at).toLocaleDateString("en-US", {
animateMetric(services, data.summary.services); month: "short",
animateMetric(vlans, data.summary.vlans); year: "numeric",
animateMetric(edge, data.summary.edgeSystems); });
grid.innerHTML = ""; return `
<div class="repo-card">
<div class="repo-card-top">
<span class="repo-icon">📁</span>
<a
class="repo-name"
href="${repo.html_url}"
target="_blank"
rel="noopener noreferrer"
title="${repo.name}"
>${repo.name}</a>
<span class="repo-visibility">${repo.private ? "private" : "public"}</span>
</div>
data.systems.forEach((system) => { <p class="repo-desc">${desc}</p>
const card = document.createElement("article");
card.className = "noc-system-card";
const status = system.status.toLowerCase(); <div class="repo-meta">
${repo.language
const statusClass = ? `<span>
status.includes("online") || status.includes("operational") <span class="repo-lang-dot" style="background:${langColor}"></span>
? "online" ${repo.language}
: status.includes("building") || status.includes("tuning") || status.includes("active") </span>`
? "warning" : ""}
: "offline"; <span>★ ${repo.stargazers_count}</span>
<span>Updated ${updatedDate}</span>
card.innerHTML = ` </div>
<div class="noc-system-top">
<span class="mini-status ${statusClass}"></span>
<span>${system.layer}</span>
</div>
<div class="system-label">${system.label || "System"}</div>
<div class="severity-pill ${system.severity?.toLowerCase() || "unknown"}">
${system.severity || "Unknown"}
</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>
<small>Last Check: ${system.lastCheck || "Manual"}</small>
</div> </div>
`; `;
}).join("");
grid.appendChild(card); } catch (err) {
}); console.warn("[GitHub] Failed to load repos:", err.message);
if (container) {
if (alertList) { container.innerHTML = `
renderAlerts(data.systems, alertList); <div class="repo-loading">
<p class="noc-loading">
Could not load repositories.
<a href="https://github.com/ced4568" target="_blank" rel="noopener noreferrer">
View on GitHub →
</a>
</p>
</div>
`;
} }
} catch (error) {
const statusText = document.getElementById("noc-status-text");
if (statusText) {
statusText.textContent = "Ceds NOC data unavailable";
}
console.error("Failed to load NOC status:", error);
} }
} }
const scored = systems
function animateMetric(element, value) { .map((sys) => {
element.classList.remove("metric-pop"); const sev = (sys.severity || "").toLowerCase();
element.textContent = value; const status = (sys.status || "").toLowerCase();
void element.offsetWidth;
element.classList.add("metric-pop");
}
function renderAlerts(systems, alertList) {
const priority = systems
.map((system) => {
const severity = (system.severity || "Unknown").toLowerCase();
const status = (system.status || "").toLowerCase();
let score = 0; let score = 0;
if (sev.includes("critical") || status.includes("offline")) score = 3;
else if (sev.includes("degraded")) score = 2;
else if (sev.includes("unknown")) score = 1;
if (severity.includes("critical") || status.includes("offline")) { return { ...sys, score };
score = 3;
} else if (severity.includes("degraded")) {
score = 2;
} else if (severity.includes("unknown")) {
score = 1;
}
return { ...system, score };
}) })
.filter((system) => system.score > 0) .filter((s) => s.score > 0)
.sort((a, b) => b.score - a.score) .sort((a, b) => b.score - a.score)
.slice(0, 3); .slice(0, 3);
if (priority.length === 0) { if (scored.length === 0) {
alertList.innerHTML = ` alertList.innerHTML = `
<div class="noc-alert healthy"> <div class="noc-alert healthy">
<strong>All monitored systems healthy</strong> <strong>All monitored systems nominal</strong>
<span>No critical or degraded systems detected.</span> <span>No critical or degraded systems detected.</span>
</div> </div>
`; `;
return; return;
} }
alertList.innerHTML = priority alertList.innerHTML = scored.map((sys) => {
.map((system) => { const cls =
const alertClass = sys.score === 3 ? "critical"
system.score === 3 ? "critical" : system.score === 2 ? "degraded" : "unknown"; : sys.score === 2 ? "degraded"
: "unknown";
return ` return `
<div class="noc-alert ${alertClass}"> <div class="noc-alert ${cls}">
<strong>${system.name}</strong> <strong>${sys.name}</strong>
<span>${system.severity || system.status}${system.layer}${system.latency || "N/A"}</span> <span>${sys.severity || sys.status}${sys.layer} Latency: ${sys.latency || "N/A"}</span>
</div> </div>
`; `;
}) }).join("");
.join(""); }
}