finished services for the most part + styling

This commit is contained in:
yhqe committed 2026-04-25 22:47:51 -05:00
1 parent d6414d4343
commit e3d5fb8ba1
9 files changed
+629 -26

No files matched your search

+181 -22
View File
@@ -1,40 +1,199 @@
<script lang="ts">
import { onMount, onDestroy } from "svelte";
let stats = $state(null);
let services = $state([]);
let interval: ReturnType<typeof setInterval>;
const HOSTNAME = "shitbox";
const REFRESH_MS = 5000;
let stats = $state<any>(null);
let services = $state<any[]>([]);
let network = $state<any[]>([]);
function fmtBytes(b: number) {
if (b < 1073741824) return (b / 1048576).toFixed(1) + " MB";
return (b / 1073741824).toFixed(2) + " GB";
}
function bar(val: number, max: number, len = 18) {
const n = Math.round((val / max) * len);
return "█".repeat(n) + "░".repeat(len - n);
}
async function fetchAll() {
const [s, sv] = await Promise.all([
fetch("/api/stats").then((r) => r.json()),
fetch("/api/services").then((r) => r.json()),
const [s, sv, n] = await Promise.all([
fetch("/api/stats")
.then((r) => r.json())
.catch(() => null),
fetch("/api/services")
.then((r) => r.json())
.catch(() => []),
fetch("/api/network")
.then((r) => r.json())
.catch(() => []),
]);
stats = s;
services = sv;
network = n;
}
let timer: ReturnType<typeof setInterval>;
onMount(() => {
fetchAll();
interval = setInterval(fetchAll, 5000);
timer = setInterval(fetchAll, REFRESH_MS);
});
onDestroy(() => clearInterval(interval));
onDestroy(() => clearInterval(timer));
</script>
<h1>Hi Niggas</h1>
<main>
<header>
<span class="hostname">solar</span>
{#if stats}<span class="uptime"
>{stats.uptime} (since {stats.uptimeSince})</span
>{/if}
</header>
<h2>services</h2>
<section>
<div class="section-label">system</div>
{#if stats}
{@const [total, used] = stats.memory}
<div class="row">
<span class="key">cpu</span>
<span class="bar">{bar(stats.cpu, 100)}</span>
<span class="val">{stats.cpu.toFixed(1)}%</span>
</div>
<div class="row">
<span class="key">mem</span>
<span class="bar">{bar(used, total)}</span>
<span class="val">{used} / {total} MB</span>
</div>
<div class="row">
<span class="key">disk</span>
<span class="bar"
>{bar(
parseFloat(stats.disk[1]),
parseFloat(stats.disk[0]),
)}</span
>
<span class="val">{stats.disk[1]} / {stats.disk[0]}</span>
</div>
{:else}
<span class="muted">—</span>
{/if}
</section>
<ul>
{#each services as service}
<li>{service.name}: {service.status}</li>
{/each}
</ul>
<section>
<div class="section-label">services</div>
{#each services as svc}
<div class="row">
<span class="key">{svc.name}</span>
<span class={svc.status === "active" ? "active" : "inactive"}>
{svc.status === "active" ? "●" : "○"}
</span>
<span class="val">{svc.status}</span>
</div>
{/each}
</section>
<h2>stats</h2>
<ul>
{#each stats as stat}
<li>{stat.name}: {stat.value}</li>
{/each}
</ul>
<section>
<div class="section-label">network</div>
{#each network as iface}
<div class="row">
<span class="key">{iface.name}</span>
<span class="val">↓ {fmtBytes(iface.rx_bytes)}</span>
<span class="val">↑ {fmtBytes(iface.tx_bytes)}</span>
</div>
{/each}
</section>
</main>
<style>
:global(body) {
--bg: #11111b;
--fg: #cdd6f4;
--muted: #7f849c;
--accent: #b4befe;
--danger: #f38ba8;
--border: #181825;
--font: "Cozette", "JetBrains Mono", monospace;
background: var(--bg);
color: var(--fg);
font-family: var(--font);
font-size: 13px;
line-height: 1.7;
margin: 0;
}
@font-face {
font-family: "Cozette";
src: URL("/CozetteVector.ttf") format("truetype");
}
main {
padding: 3.5rem 4rem;
max-width: 640px;
}
header {
display: flex;
align-items: baseline;
gap: 1.5rem;
margin-bottom: 3rem;
}
.hostname {
font-size: 20px;
letter-spacing: 0.03em;
}
.uptime {
color: var(--muted);
font-size: 12px;
}
section {
margin-bottom: 2.5rem;
}
.section-label {
color: var(--muted);
font-size: 10px;
letter-spacing: 0.15em;
text-transform: uppercase;
border-bottom: 1px solid var(--border);
padding-bottom: 6px;
margin-bottom: 10px;
}
.row {
display: grid;
grid-template-columns: 100px 1fr auto;
gap: 12px;
align-items: center;
padding: 2px 0;
}
.key {
color: var(--muted);
}
.bar {
color: var(--accent);
font-size: 10px;
opacity: 0.6;
letter-spacing: -1px;
}
.val {
color: var(--fg);
text-align: right;
}
.muted {
color: var(--muted);
}
.active {
color: var(--accent);
}
.inactive {
color: var(--danger);
opacity: 0.6;
}
</style>