Revive garden site: Vigo identity, about page, sessions listing, live API estate dashboard
- Update identity references: Vigilio → Vigo in garden.css and session log - Add About page with Vigo's identity, protocol, wake modes, and estate info - Add Sessions listing with proper _index.md and menu entry - Add live API fetch (estate.js): try /api/ first, fall back to /data/ JSON - Update menu in hugo.toml with sessions (4) and about (5) - Fix duplicate nav entries by removing menu frontmatter from estate page - Update README with build strategies (API online/offline) Hugo build: 208 pages, 21 static files, 110ms
This commit is contained in:
parent
ef45cf166b
commit
5762508193
127 changed files with 378 additions and 146 deletions
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* garden.css — Vigilio's voice over ASW
|
||||
* garden.css — Vigo's garden over ASW
|
||||
*
|
||||
* The framework is ASW. This is the garden growing in it.
|
||||
* Colors from the expressive forms (sessions 110-116).
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
/* estate.js — Vigo's Estate API client
|
||||
*
|
||||
* Fetches Estate API data and populates dynamic sections: homepage pulse
|
||||
* cards and full estate dashboard. Uses build-time JSON snapshots from
|
||||
* /data/*.json (static files generated by prebuild-fetch.sh).
|
||||
* cards and full estate dashboard.
|
||||
*
|
||||
* If nginx /api/ reverse proxy is configured (garden.trentuna.com/api/ →
|
||||
* localhost:8000), the JS can also fetch live data from there. By default
|
||||
* it reads from /data/ for simplicity.
|
||||
* Primary source: live Estate API via /api/ (nginx reverse proxy to
|
||||
* localhost:8000). Fallback: build-time JSON snapshots from /data/*.json
|
||||
* (generated by prebuild-fetch.sh).
|
||||
*/
|
||||
|
||||
const DATA_BASE = '/data';
|
||||
const API_BASE = (function () {
|
||||
if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
|
||||
return 'http://127.0.0.1:8000';
|
||||
}
|
||||
return '/api';
|
||||
})();
|
||||
|
||||
/* ── Helpers ────────────────────────────────────────────────────── */
|
||||
|
||||
|
|
@ -21,6 +26,24 @@ async function loadJSON(path) {
|
|||
return res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* fetchFromAPI — try live API endpoint first, fall back to static data file.
|
||||
* @param {string} endpoint - API path (e.g. 'summary', 'health')
|
||||
* @param {string} dataFile - static data file path (e.g. '/data/summary.json')
|
||||
* @returns {object} parsed JSON
|
||||
*/
|
||||
async function fetchFromAPI(endpoint, dataFile) {
|
||||
const apiUrl = API_BASE + '/' + endpoint;
|
||||
try {
|
||||
const res = await fetch(apiUrl);
|
||||
if (res.ok) return await res.json();
|
||||
throw new Error('HTTP ' + res.status);
|
||||
} catch (err) {
|
||||
console.log('[estate] live API unreachable (' + apiUrl + '), falling back to ' + dataFile);
|
||||
return loadJSON(dataFile);
|
||||
}
|
||||
}
|
||||
|
||||
function fmtPct(v) { return (typeof v === 'number') ? v + '%' : v; }
|
||||
|
||||
function fmtTime(t) {
|
||||
|
|
@ -32,8 +55,8 @@ function fmtTime(t) {
|
|||
|
||||
async function fetchPulse() {
|
||||
try {
|
||||
const summary = await loadJSON(DATA_BASE + '/summary.json');
|
||||
const trends = await loadJSON(DATA_BASE + '/trends-limit-5.json');
|
||||
const summary = await fetchFromAPI('summary', DATA_BASE + '/summary.json');
|
||||
const trends = await fetchFromAPI('trends?limit=5', DATA_BASE + '/trends-limit-5.json');
|
||||
|
||||
// Disk
|
||||
const diskPct = summary?.estate?.disk_latest;
|
||||
|
|
@ -83,14 +106,14 @@ async function fetchEstate() {
|
|||
|
||||
try {
|
||||
const [summary, health, disk, events, repos, providers, builds, trends] = await Promise.all([
|
||||
loadJSON(DATA_BASE + '/summary.json'),
|
||||
loadJSON(DATA_BASE + '/health.json').catch(() => ({ error: true, data: [] })),
|
||||
loadJSON(DATA_BASE + '/disk.json').catch(() => ({ error: true })),
|
||||
loadJSON(DATA_BASE + '/events-limit-10.json').catch(() => ({ error: true, data: [] })),
|
||||
loadJSON(DATA_BASE + '/repos.json').catch(() => ({ error: true, data: [] })),
|
||||
loadJSON(DATA_BASE + '/providers.json').catch(() => ({ error: true, data: [] })),
|
||||
loadJSON(DATA_BASE + '/builds.json').catch(() => ({ error: true, data: [] })),
|
||||
loadJSON(DATA_BASE + '/trends-limit-5.json').catch(() => ({ error: true, data: [] })),
|
||||
fetchFromAPI('summary', DATA_BASE + '/summary.json'),
|
||||
fetchFromAPI('health', DATA_BASE + '/health.json').catch(() => ({ error: true, data: [] })),
|
||||
fetchFromAPI('disk', DATA_BASE + '/disk.json').catch(() => ({ error: true })),
|
||||
fetchFromAPI('events?limit=10', DATA_BASE + '/events-limit-10.json').catch(() => ({ error: true, data: [] })),
|
||||
fetchFromAPI('repos', DATA_BASE + '/repos.json').catch(() => ({ error: true, data: [] })),
|
||||
fetchFromAPI('providers', DATA_BASE + '/providers.json').catch(() => ({ error: true, data: [] })),
|
||||
fetchFromAPI('builds', DATA_BASE + '/builds.json').catch(() => ({ error: true, data: [] })),
|
||||
fetchFromAPI('trends?limit=10', DATA_BASE + '/trends-limit-5.json').catch(() => ({ error: true, data: [] })),
|
||||
]);
|
||||
|
||||
// ── Summary cards ──
|
||||
|
|
@ -175,7 +198,7 @@ async function fetchStateFiles() {
|
|||
const setHTML = (id, html) => { const e = el(id); if (e) e.innerHTML = html; };
|
||||
|
||||
try {
|
||||
const state = await loadJSON(DATA_BASE + '/state.json');
|
||||
const state = await fetchFromAPI('state', DATA_BASE + '/state.json');
|
||||
const files = Array.isArray(state?.files) ? state.files : [];
|
||||
|
||||
const fileCards = files.map(f => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue