homelab: Plattenschwellen 80/90 % statt 85/95 % (ext4 haelt 5 % fuer root zurueck; AdGuard war bei 94 % innen voll)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-24 21:04:05 +02:00
co-authored by Claude Opus 5.5
parent c08a144006
commit 6a5134ef79
20 changed files with 24 additions and 24 deletions
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{c as e,i as t,p as n,s as r,t as i,u as a}from"./button-BheHBEml.js";import{B as o,Ct as s,T as c,ht as l,lt as u,ot as d}from"./index-BAH01Yfc.js";import{a as f,i as p,n as m,r as h,t as g}from"./sheet-CKp82pOA.js";var _=r(),v=n(a(),1),y=e();function b(e){let t=(0,_.c)(4),{unit:n}=e,r=o(n),i;if(r.isPending)i=`Wird gelesen …`;else if(r.isError)i=`Das Protokoll lässt sich gerade nicht lesen: ${r.error.message}`;else if(r.data.ok===!1)i=r.data.err||r.data.text||`Das Protokoll lässt sich nicht lesen.`;else{let e;t[0]===r.data.text?e=t[1]:(e=r.data.text?.trim()||`Noch keine Einträge.`,t[0]=r.data.text,t[1]=e),i=e}let a;return t[2]===i?a=t[3]:(a=(0,y.jsx)(`pre`,{className:`ziffern m-0 max-h-[50vh] w-full basis-full overflow-auto rounded-lg border border-linie bg-[#0b0d0f] p-3 text-xs leading-relaxed whitespace-pre-wrap break-all text-text-2`,children:i}),t[2]=i,t[3]=a),a}function x({offen:e,onSchliessen:n}){let r=s(),a=c(),[o,_]=(0,v.useState)(null),[x,S]=(0,v.useState)(null),[C,w]=(0,v.useState)(null);async function T(e){if(!C){S(null),w(e.unit);try{let t=await l(`/api/maintenance/restart`,{service:e.unit});t.ok?d(`erfolg`,`${e.name} ${e.schlaeft?`wird geweckt`:`startet neu`}.`):d(`fehler`,`${e.name} ließ sich nicht starten: ${u(t)}`),r.invalidateQueries({queryKey:[`dienste`]})}catch(e){d(`fehler`,e.message)}finally{w(null)}}}let E;return E=a.isPending?(0,y.jsx)(`p`,{role:`status`,className:`px-4 text-[15px] text-text-2`,children:`Dienste werden gelesen …`}):a.isError&&!a.data?(0,y.jsxs)(`p`,{role:`alert`,className:`px-4 text-[15px] text-rot-text`,children:[`Die Dienste lassen sich gerade nicht lesen: `,a.error.message]}):a.data?.services.length?(0,y.jsx)(`ul`,{className:`m-0 flex list-none flex-col px-4 pb-4`,children:a.data.services.map(e=>(0,y.jsxs)(`li`,{className:`flex flex-wrap items-center justify-between gap-2 border-t border-linie py-2.5`,children:[(0,y.jsxs)(`span`,{className:`flex min-w-0 items-center gap-2.5`,children:[(0,y.jsx)(`span`,{className:t(`size-2.5 shrink-0 rounded-full`,e.ok?`bg-gruen`:e.schlaeft?`bg-text-3`:`bg-rot`),"aria-hidden":!0}),(0,y.jsxs)(`span`,{className:`min-w-0`,children:[(0,y.jsx)(`span`,{className:`block text-[15px]`,children:e.name}),(0,y.jsxs)(`span`,{className:`ziffern text-xs text-text-3`,children:[e.unit,` · `,e.ok?`läuft`:e.schlaeft?`schläft (abgeschaltet)`:`antwortet nicht`]})]})]}),(0,y.jsxs)(`span`,{className:`flex flex-wrap gap-2`,children:[(0,y.jsx)(i,{variant:o===e.unit?`info`:`ghost`,size:`sm`,"aria-expanded":o===e.unit,onClick:()=>_(o===e.unit?null:e.unit),children:`Protokoll`}),x===e.unit?(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(i,{variant:`gefahr`,size:`sm`,onClick:()=>T(e),children:e.schlaeft?`Wirklich wecken?`:`Wirklich neu starten?`}),(0,y.jsx)(i,{variant:`ghost`,size:`sm`,onClick:()=>S(null),children:`Nein`})]}):(0,y.jsx)(i,{variant:`outline`,size:`sm`,disabled:C!==null,onClick:()=>S(e.unit),children:C===e.unit?`Startet …`:e.schlaeft?`Wecken`:`Neu starten`})]}),o===e.unit&&(0,y.jsx)(b,{unit:e.unit})]},e.unit))}):(0,y.jsx)(`p`,{className:`px-4 text-[15px] text-text-2`,children:`Keine Dienste gefunden.`}),(0,y.jsx)(g,{open:e,onOpenChange:e=>!e&&n(),children:(0,y.jsxs)(m,{side:`right`,className:`gap-3 overflow-y-auto border-linie bg-panel data-[side=right]:w-full data-[side=right]:sm:max-w-2xl`,children:[(0,y.jsxs)(p,{children:[(0,y.jsx)(f,{className:`schild text-lg`,children:`Dienste und Protokolle`}),(0,y.jsx)(h,{className:`text-text-2`,children:`Welcher Dienst läuft, was er zuletzt geschrieben hat.`})]}),E]})})}export{x as Dienste};
import{c as e,i as t,p as n,s as r,t as i,u as a}from"./button-BheHBEml.js";import{B as o,Ct as s,T as c,ht as l,lt as u,ot as d}from"./index-C6xT7cKA.js";import{a as f,i as p,n as m,r as h,t as g}from"./sheet-CKp82pOA.js";var _=r(),v=n(a(),1),y=e();function b(e){let t=(0,_.c)(4),{unit:n}=e,r=o(n),i;if(r.isPending)i=`Wird gelesen …`;else if(r.isError)i=`Das Protokoll lässt sich gerade nicht lesen: ${r.error.message}`;else if(r.data.ok===!1)i=r.data.err||r.data.text||`Das Protokoll lässt sich nicht lesen.`;else{let e;t[0]===r.data.text?e=t[1]:(e=r.data.text?.trim()||`Noch keine Einträge.`,t[0]=r.data.text,t[1]=e),i=e}let a;return t[2]===i?a=t[3]:(a=(0,y.jsx)(`pre`,{className:`ziffern m-0 max-h-[50vh] w-full basis-full overflow-auto rounded-lg border border-linie bg-[#0b0d0f] p-3 text-xs leading-relaxed whitespace-pre-wrap break-all text-text-2`,children:i}),t[2]=i,t[3]=a),a}function x({offen:e,onSchliessen:n}){let r=s(),a=c(),[o,_]=(0,v.useState)(null),[x,S]=(0,v.useState)(null),[C,w]=(0,v.useState)(null);async function T(e){if(!C){S(null),w(e.unit);try{let t=await l(`/api/maintenance/restart`,{service:e.unit});t.ok?d(`erfolg`,`${e.name} ${e.schlaeft?`wird geweckt`:`startet neu`}.`):d(`fehler`,`${e.name} ließ sich nicht starten: ${u(t)}`),r.invalidateQueries({queryKey:[`dienste`]})}catch(e){d(`fehler`,e.message)}finally{w(null)}}}let E;return E=a.isPending?(0,y.jsx)(`p`,{role:`status`,className:`px-4 text-[15px] text-text-2`,children:`Dienste werden gelesen …`}):a.isError&&!a.data?(0,y.jsxs)(`p`,{role:`alert`,className:`px-4 text-[15px] text-rot-text`,children:[`Die Dienste lassen sich gerade nicht lesen: `,a.error.message]}):a.data?.services.length?(0,y.jsx)(`ul`,{className:`m-0 flex list-none flex-col px-4 pb-4`,children:a.data.services.map(e=>(0,y.jsxs)(`li`,{className:`flex flex-wrap items-center justify-between gap-2 border-t border-linie py-2.5`,children:[(0,y.jsxs)(`span`,{className:`flex min-w-0 items-center gap-2.5`,children:[(0,y.jsx)(`span`,{className:t(`size-2.5 shrink-0 rounded-full`,e.ok?`bg-gruen`:e.schlaeft?`bg-text-3`:`bg-rot`),"aria-hidden":!0}),(0,y.jsxs)(`span`,{className:`min-w-0`,children:[(0,y.jsx)(`span`,{className:`block text-[15px]`,children:e.name}),(0,y.jsxs)(`span`,{className:`ziffern text-xs text-text-3`,children:[e.unit,` · `,e.ok?`läuft`:e.schlaeft?`schläft (abgeschaltet)`:`antwortet nicht`]})]})]}),(0,y.jsxs)(`span`,{className:`flex flex-wrap gap-2`,children:[(0,y.jsx)(i,{variant:o===e.unit?`info`:`ghost`,size:`sm`,"aria-expanded":o===e.unit,onClick:()=>_(o===e.unit?null:e.unit),children:`Protokoll`}),x===e.unit?(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(i,{variant:`gefahr`,size:`sm`,onClick:()=>T(e),children:e.schlaeft?`Wirklich wecken?`:`Wirklich neu starten?`}),(0,y.jsx)(i,{variant:`ghost`,size:`sm`,onClick:()=>S(null),children:`Nein`})]}):(0,y.jsx)(i,{variant:`outline`,size:`sm`,disabled:C!==null,onClick:()=>S(e.unit),children:C===e.unit?`Startet …`:e.schlaeft?`Wecken`:`Neu starten`})]}),o===e.unit&&(0,y.jsx)(b,{unit:e.unit})]},e.unit))}):(0,y.jsx)(`p`,{className:`px-4 text-[15px] text-text-2`,children:`Keine Dienste gefunden.`}),(0,y.jsx)(g,{open:e,onOpenChange:e=>!e&&n(),children:(0,y.jsxs)(m,{side:`right`,className:`gap-3 overflow-y-auto border-linie bg-panel data-[side=right]:w-full data-[side=right]:sm:max-w-2xl`,children:[(0,y.jsxs)(p,{children:[(0,y.jsx)(f,{className:`schild text-lg`,children:`Dienste und Protokolle`}),(0,y.jsx)(h,{className:`text-text-2`,children:`Welcher Dienst läuft, was er zuletzt geschrieben hat.`})]}),E]})})}export{x as Dienste};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{c as e,s as t}from"./button-BheHBEml.js";import{a as n,o as r}from"./index-BAH01Yfc.js";import{a as i,i as a,n as o,r as s,t as c}from"./sheet-CKp82pOA.js";var l=t(),u=e();function d(e){let t=(0,l.c)(10),{onDienste:d,onEinstellungen:f,onSchliessen:p}=e,m;t[0]===p?m=t[1]:(m=e=>!e&&p(),t[0]=p,t[1]=m);let h;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(h=(0,u.jsxs)(a,{className:`p-0`,children:[(0,u.jsx)(i,{asChild:!0,children:(0,u.jsx)(`div`,{children:(0,u.jsx)(n,{})})}),(0,u.jsx)(s,{className:`sr-only`,children:`Bereiche und Seiten`})]}),t[2]=h):h=t[2];let g;t[3]!==d||t[4]!==f||t[5]!==p?(g=(0,u.jsxs)(o,{side:`left`,className:`flex flex-col gap-6 border-linie bg-[#0c0f12] px-4 pt-5 pb-[calc(1.25rem+env(safe-area-inset-bottom))] data-[side=left]:w-[300px]`,children:[h,(0,u.jsx)(r,{onDienste:d,onEinstellungen:f,onGewaehlt:p})]}),t[3]=d,t[4]=f,t[5]=p,t[6]=g):g=t[6];let _;return t[7]!==m||t[8]!==g?(_=(0,u.jsx)(c,{open:!0,onOpenChange:m,children:g}),t[7]=m,t[8]=g,t[9]=_):_=t[9],_}export{d as MenueSchublade};
import{c as e,s as t}from"./button-BheHBEml.js";import{a as n,o as r}from"./index-C6xT7cKA.js";import{a as i,i as a,n as o,r as s,t as c}from"./sheet-CKp82pOA.js";var l=t(),u=e();function d(e){let t=(0,l.c)(10),{onDienste:d,onEinstellungen:f,onSchliessen:p}=e,m;t[0]===p?m=t[1]:(m=e=>!e&&p(),t[0]=p,t[1]=m);let h;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(h=(0,u.jsxs)(a,{className:`p-0`,children:[(0,u.jsx)(i,{asChild:!0,children:(0,u.jsx)(`div`,{children:(0,u.jsx)(n,{})})}),(0,u.jsx)(s,{className:`sr-only`,children:`Bereiche und Seiten`})]}),t[2]=h):h=t[2];let g;t[3]!==d||t[4]!==f||t[5]!==p?(g=(0,u.jsxs)(o,{side:`left`,className:`flex flex-col gap-6 border-linie bg-[#0c0f12] px-4 pt-5 pb-[calc(1.25rem+env(safe-area-inset-bottom))] data-[side=left]:w-[300px]`,children:[h,(0,u.jsx)(r,{onDienste:d,onEinstellungen:f,onGewaehlt:p})]}),t[3]=d,t[4]=f,t[5]=p,t[6]=g):g=t[6];let _;return t[7]!==m||t[8]!==g?(_=(0,u.jsx)(c,{open:!0,onOpenChange:m,children:g}),t[7]=m,t[8]=g,t[9]=_):_=t[9],_}export{d as MenueSchublade};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -7,9 +7,9 @@
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>Homelab Orchestrator</title>
<script type="module" crossorigin src="/assets/index-BAH01Yfc.js"></script>
<script type="module" crossorigin src="/assets/index-C6xT7cKA.js"></script>
<link rel="modulepreload" crossorigin href="/assets/button-BheHBEml.js">
<link rel="stylesheet" crossorigin href="/assets/index-Cb8UJMqV.css">
<link rel="stylesheet" crossorigin href="/assets/index-BGR43ZZ7.css">
</head>
<body>
<div id="root"></div>␍
@@ -42,7 +42,7 @@ function SystemText({ b }: { b: BausteinStand }) {
)
}
/** Belegung der Platte: ruhig bis 85 %, dann bernstein, ab 95 % rot. */
/** Belegung der Platte: ruhig bis 80 %, dann bernstein, ab 90 % rot. */
function PlatteZelle({ ziel }: { ziel: ZielStand }) {
const anteil = plattenAnteil(ziel)
if (anteil === null) return <span className="text-text-3">–</span>
@@ -81,7 +81,7 @@ describe("GeraeteListe", () => {
expect(start).toHaveBeenCalledWith(suchen)
})
it("zeigt die Belegung der Platte und sagt „voll“ ab 95 %", () => {
it("zeigt die Belegung der Platte und sagt „voll“ ab 90 %", () => {
render(<GeraeteListe ziele={[{ ...netbird, platte: { belegt: 1_960_000_000, gesamt: 2_000_000_000 } }]}
laufend={new Map()} gesperrt={false} onAktion={() => {}} />)
expect(screen.getByText("98 % voll")).toHaveAttribute("title", "2,0 von 2,0 GB belegt")
+1 -1
View File
@@ -55,7 +55,7 @@ describe("lampeFuer", () => {
it("warnt bei voller Platte vor allem anderen außer Stille", () => {
expect(lampeFuer({ ...netbird, platte: { belegt: 98, gesamt: 100 } }, true)).toMatchObject({ zustand: "fehler", wert: "Platte voll" })
expect(lampeFuer({ ...npm, platte: { belegt: 90, gesamt: 100 } }, false)).toMatchObject({ zustand: "warn", wert: "Platte 90 %" })
expect(lampeFuer({ ...npm, platte: { belegt: 85, gesamt: 100 } }, false)).toMatchObject({ zustand: "warn", wert: "Platte 85 %" })
expect(lampeFuer({ ...npm, platte: { belegt: 50, gesamt: 100 } }, false).zustand).toBe("ok")
})
+3 -3
View File
@@ -81,14 +81,14 @@ export function neuText(b: BausteinStand): string {
return b.kurz || b.verfuegbar || "neu"
}
/** Anteil der belegten Platte (0…1) oder null, wo unbekannt. Ab 85 % gelb, ab 95 % rot — wie der Wächter. */
/** Anteil der belegten Platte (0…1) oder null, wo unbekannt. Ab 80 % gelb, ab 90 % rot — wie der Wächter (ext4 hält 5 % zurück). */
export function plattenAnteil(ziel: ZielStand): number | null {
const p = ziel.platte
return p && p.gesamt > 0 ? p.belegt / p.gesamt : null
}
export const PLATTE_GELB = 0.85
export const PLATTE_ROT = 0.95
export const PLATTE_GELB = 0.8
export const PLATTE_ROT = 0.9
export function lampeFuer(ziel: ZielStand, laeuft: boolean): Lampe {
const label = kurzname(ziel)