Fix dashboard BigInt crash when storage fields are missing.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
39ecf1af56
commit
9b557cee54
2 changed files with 39 additions and 15 deletions
|
|
@ -15,10 +15,17 @@ interface Dashboard {
|
||||||
currentBandwidth: string;
|
currentBandwidth: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatBytes(bytes: string): string {
|
function formatBytes(bytes: string | number | null | undefined): string {
|
||||||
const n = BigInt(bytes);
|
if (bytes === null || bytes === undefined || bytes === "") return "0 B";
|
||||||
|
let n: bigint;
|
||||||
|
try {
|
||||||
|
n = typeof bytes === "bigint" ? bytes : BigInt(String(bytes));
|
||||||
|
} catch {
|
||||||
|
return "0 B";
|
||||||
|
}
|
||||||
const units = ["B", "KB", "MB", "GB", "TB"];
|
const units = ["B", "KB", "MB", "GB", "TB"];
|
||||||
let val = Number(n);
|
let val = Number(n);
|
||||||
|
if (!Number.isFinite(val)) return "0 B";
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (val >= 1024 && i < units.length - 1) {
|
while (val >= 1024 && i < units.length - 1) {
|
||||||
val /= 1024;
|
val /= 1024;
|
||||||
|
|
@ -30,12 +37,21 @@ function formatBytes(bytes: string): string {
|
||||||
export default function DashboardPage() {
|
export default function DashboardPage() {
|
||||||
useAuth();
|
useAuth();
|
||||||
const [data, setData] = useState<Dashboard | null>(null);
|
const [data, setData] = useState<Dashboard | null>(null);
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch("/api/v1/admin/dashboard", { credentials: "include" })
|
fetch("/api/v1/admin/dashboard", { credentials: "include" })
|
||||||
.then((r) => r.json())
|
.then(async (r) => {
|
||||||
.then(setData)
|
const json = await r.json();
|
||||||
.catch(console.error);
|
if (!r.ok) {
|
||||||
|
throw new Error(json.error?.message ?? `HTTP ${r.status}`);
|
||||||
|
}
|
||||||
|
setData(json);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
setError(err instanceof Error ? err.message : "Laden mislukt");
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -43,42 +59,48 @@ export default function DashboardPage() {
|
||||||
<Nav />
|
<Nav />
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<h1 style={{ marginBottom: "2rem", fontSize: "1.75rem" }}>Dashboard</h1>
|
<h1 style={{ marginBottom: "2rem", fontSize: "1.75rem" }}>Dashboard</h1>
|
||||||
|
{error && <div className="error">{error}</div>}
|
||||||
{data && (
|
{data && (
|
||||||
<div className="grid">
|
<div className="grid">
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h2>Nodes Online</h2>
|
<h2>Nodes Online</h2>
|
||||||
<div className="value">{data.nodesOnline}</div>
|
<div className="value">{data.nodesOnline ?? 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h2>Nodes Offline</h2>
|
<h2>Nodes Offline</h2>
|
||||||
<div className="value">{data.nodesOffline}</div>
|
<div className="value">{data.nodesOffline ?? 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h2>Movies</h2>
|
<h2>Movies</h2>
|
||||||
<div className="value">{data.movies}</div>
|
<div className="value">{data.movies ?? 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h2>Series</h2>
|
<h2>Series</h2>
|
||||||
<div className="value">{data.series}</div>
|
<div className="value">{data.series ?? 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h2>Episodes</h2>
|
<h2>Episodes</h2>
|
||||||
<div className="value">{data.episodes}</div>
|
<div className="value">{data.episodes ?? 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h2>Active Streams</h2>
|
<h2>Active Streams</h2>
|
||||||
<div className="value">{data.activeStreams}</div>
|
<div className="value">{data.activeStreams ?? 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h2>Total Storage</h2>
|
<h2>Total Storage</h2>
|
||||||
<div className="value" style={{ fontSize: "1.5rem" }}>{formatBytes(data.totalStorage)}</div>
|
<div className="value" style={{ fontSize: "1.5rem" }}>
|
||||||
|
{formatBytes(data.totalStorage)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h2>Free Storage</h2>
|
<h2>Free Storage</h2>
|
||||||
<div className="value" style={{ fontSize: "1.5rem" }}>{formatBytes(data.freeStorage)}</div>
|
<div className="value" style={{ fontSize: "1.5rem" }}>
|
||||||
|
{formatBytes(data.freeStorage)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{!data && !error && <p style={{ color: "#71717a" }}>Laden...</p>}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,10 @@ interface Movie {
|
||||||
files: MovieFile[];
|
files: MovieFile[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatBytes(bytes: string): string {
|
function formatBytes(bytes: string | number | null | undefined): string {
|
||||||
let val = Number(bytes);
|
const valNum = Number(bytes ?? 0);
|
||||||
|
if (!Number.isFinite(valNum) || valNum < 0) return "0 B";
|
||||||
|
let val = valNum;
|
||||||
const units = ["B", "KB", "MB", "GB", "TB"];
|
const units = ["B", "KB", "MB", "GB", "TB"];
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (val >= 1024 && i < units.length - 1) {
|
while (val >= 1024 && i < units.length - 1) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue