109 lines
3.4 KiB
JavaScript
109 lines
3.4 KiB
JavaScript
async function loadConfig() {
|
|
const response = await fetch('config/services.json', { cache: 'no-store' });
|
|
if (!response.ok) throw new Error(`Config load failed: ${response.status}`);
|
|
return response.json();
|
|
}
|
|
|
|
function renderStatus(items) {
|
|
const grid = document.getElementById('status-grid');
|
|
grid.innerHTML = items.map(item => `
|
|
<article class="status-card">
|
|
<div class="status-top">
|
|
<span class="status-name">${item.name}</span>
|
|
<span class="status-state ${item.state}">
|
|
<span class="status-dot"></span>${item.label}
|
|
</span>
|
|
</div>
|
|
<div class="status-value">${item.value}</div>
|
|
<div class="status-detail">${item.detail}</div>
|
|
</article>
|
|
`).join('');
|
|
}
|
|
|
|
function renderServices(targetId, items, disabled = false) {
|
|
const grid = document.getElementById(targetId);
|
|
grid.innerHTML = items.map(item => {
|
|
const classes = `service-card${disabled ? ' disabled' : ''}`;
|
|
const tag = disabled ? 'div' : 'a';
|
|
const href = disabled ? '' : ` href="${item.url}"`;
|
|
return `
|
|
<${tag} class="${classes}"${href}>
|
|
<div class="service-icon">${item.icon}</div>
|
|
<h3>${item.name}</h3>
|
|
<p>${item.description}</p>
|
|
<div class="service-foot">
|
|
<span>${item.category}</span>
|
|
<span>${disabled ? 'Pending' : 'Open ↗'}</span>
|
|
</div>
|
|
</${tag}>
|
|
`;
|
|
}).join('');
|
|
}
|
|
|
|
loadConfig()
|
|
Promise.all([loadConfig(), loadTelemetry()])
|
|
.then(([config, telemetry]) => {
|
|
const staticStatus = config.status.filter(
|
|
item => !['Backups', 'Git'].includes(item.name)
|
|
);
|
|
|
|
renderStatus([
|
|
...staticStatus,
|
|
...telemetryStatus(telemetry.systems)
|
|
]);
|
|
|
|
renderServices('service-grid', config.services);
|
|
renderServices('home-grid', config.home, true);
|
|
})
|
|
.catch(error => {
|
|
console.error(error);
|
|
document.getElementById('status-grid').innerHTML =
|
|
'<article class="status-card"><div class="status-name">Telemetry</div><div class="status-value">Unavailable</div><div class="status-detail">Could not load Voyager data</div></article>';
|
|
});
|
|
async function loadTelemetry() {
|
|
const response = await fetch('data/status.json', { cache: 'no-store' });
|
|
if (!response.ok) throw new Error(`Telemetry load failed: ${response.status}`);
|
|
return response.json();
|
|
}
|
|
|
|
function telemetryStatus(systems) {
|
|
return [
|
|
{
|
|
name: 'Backups',
|
|
state: systems.backup.state,
|
|
label: systems.backup.summary,
|
|
value: systems.backup.detail,
|
|
detail: 'Restic backup repository'
|
|
},
|
|
{
|
|
name: 'Git',
|
|
state: systems.git.state,
|
|
label: systems.git.state === 'online' ? 'Healthy' : 'Attention',
|
|
value: systems.git.detail,
|
|
detail: systems.git.summary
|
|
},
|
|
{
|
|
name: 'Docker',
|
|
state: systems.docker.state,
|
|
label: systems.docker.state === 'online' ? 'Healthy' : 'Attention',
|
|
value: systems.docker.summary,
|
|
detail: systems.docker.detail
|
|
},
|
|
{
|
|
name: 'Certificates',
|
|
state: systems.certificates.state,
|
|
label: systems.certificates.state === 'online' ? 'Healthy' : 'Attention',
|
|
value: systems.certificates.detail,
|
|
detail: systems.certificates.summary
|
|
},
|
|
{
|
|
name: 'System',
|
|
state: systems.system.state,
|
|
label: systems.system.state === 'online' ? 'Healthy' : 'Attention',
|
|
value: systems.system.summary,
|
|
detail: systems.system.detail
|
|
},
|
|
|
|
];
|
|
}
|