feat(admin): overview dashboard sections + system temperature/load sensors (0.85.0) (#11)
All checks were successful
deploy / test-backend (push) Successful in 25m9s
deploy / test-frontend (push) Successful in 10m13s
deploy / build-and-push (push) Successful in 10m25s
deploy / deploy (push) Successful in 12s

This commit was merged in pull request #11.
This commit is contained in:
2026-06-16 18:46:28 +00:00
parent 8445f338f6
commit cde4aca98b
17 changed files with 1270 additions and 59 deletions

View File

@@ -0,0 +1,43 @@
import { describe, it, expect } from 'vitest';
import { pickCpuTemp } from './temps';
import type { TempStat } from '$lib/api/admin';
function t(label: string, celsius: number): TempStat {
return { label, celsius, max_celsius: celsius, critical_celsius: 100 };
}
describe('pickCpuTemp', () => {
it('returns null when there are no sensors', () => {
expect(pickCpuTemp([])).toBeNull();
});
it('prefers the Intel package sensor over individual cores', () => {
const temps = [
t('coretemp Core 0', 50),
t('coretemp Core 1', 49),
t('coretemp Package id 0', 51),
t('acpitz temp1', 28)
];
expect(pickCpuTemp(temps)?.label).toBe('coretemp Package id 0');
});
it('falls back to the AMD Tctl/Tdie sensor when there is no package', () => {
const temps = [t('k10temp Tctl', 55), t('nvme Composite', 41)];
expect(pickCpuTemp(temps)?.label).toBe('k10temp Tctl');
});
it('falls back to the hottest core when only per-core sensors exist', () => {
const temps = [t('coretemp Core 0', 50), t('coretemp Core 3', 58), t('coretemp Core 1', 49)];
expect(pickCpuTemp(temps)?.label).toBe('coretemp Core 3');
});
it('falls back to any cpu/coretemp-labelled sensor as a last resort', () => {
const temps = [t('nvme Composite', 41), t('cpu_thermal', 47)];
expect(pickCpuTemp(temps)?.label).toBe('cpu_thermal');
});
it('returns null when nothing looks like a CPU sensor', () => {
const temps = [t('nvme Composite', 41), t('acpitz temp1', 28)];
expect(pickCpuTemp(temps)).toBeNull();
});
});

View File

@@ -0,0 +1,25 @@
import type { TempStat } from '$lib/api/admin';
/** Pick the sensor that best represents the CPU die/package temperature
* from a heterogeneous component list. Intel exposes
* `coretemp Package id 0`; AMD exposes `k10temp Tctl`/`Tdie`. When neither
* is present we fall back to the hottest individual core, then to any
* CPU-ish sensor, and finally `null` (nothing CPU-related — e.g. only NVMe
* / chipset sensors are exposed). Used to keep the System tab focused on
* the one reading operators care about, with the rest tucked away. */
export function pickCpuTemp(temps: TempStat[]): TempStat | null {
const byLabel = (re: RegExp) => temps.find((t) => re.test(t.label)) ?? null;
return (
byLabel(/package/i) ??
byLabel(/tctl|tdie|k10temp/i) ??
hottestCore(temps) ??
byLabel(/cpu|coretemp/i) ??
null
);
}
function hottestCore(temps: TempStat[]): TempStat | null {
const cores = temps.filter((t) => /core\s*\d+/i.test(t.label));
if (cores.length === 0) return null;
return cores.reduce((a, b) => (b.celsius > a.celsius ? b : a));
}