generated from Dicken/dickendock
round28: Portscan-Retry fuer bekannte Dienste (Echo/Samsung-Falschmeldungen behoben), 7-Tage-Schwelle jetzt auch fuer einzelne Dienste, geraeteuebergreifender Scan-Status in der Navigation
This commit is contained in:
48
apps/frontend/src/hooks/useScanActivity.ts
Normal file
48
apps/frontend/src/hooks/useScanActivity.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface ScanActivity {
|
||||
running: boolean;
|
||||
pendingCount: number;
|
||||
}
|
||||
|
||||
function readJson<T>(key: string, fallback: T): T {
|
||||
try {
|
||||
const raw = sessionStorage.getItem(`launchpad:${key}`);
|
||||
return raw !== null ? (JSON.parse(raw) as T) : fallback;
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
function computeActivity(): ScanActivity {
|
||||
const running = readJson("scanner:bulkRunning", false);
|
||||
const staleServices = readJson<unknown[]>("scanner:bulkStaleServices", []);
|
||||
const nameChanges = readJson<unknown[]>("scanner:bulkNameChanges", []);
|
||||
const staleDevices = readJson<unknown[]>("scanner:staleDevices", []);
|
||||
const deviceChanges = readJson<unknown[]>("scanner:deviceChanges", []);
|
||||
const pendingCount =
|
||||
staleServices.length + nameChanges.length + staleDevices.length + deviceChanges.length;
|
||||
return { running, pendingCount };
|
||||
}
|
||||
|
||||
/**
|
||||
* Geräteübergreifender Hinweis, ob gerade ein Scan läuft oder es
|
||||
* unbestätigte Scan-Ergebnisse gibt (siehe Scanner-Seite, dort alles über
|
||||
* usePersistedState/sessionStorage gehalten) - für ein Abzeichen am
|
||||
* "Scanner"-Navigationspunkt, sichtbar von jeder Admin-Seite aus.
|
||||
*
|
||||
* sessionStorage-Änderungen aus einer ANDEREN Komponente im selben Tab
|
||||
* lösen kein "storage"-Event aus (das feuert nur tab-übergreifend) - daher
|
||||
* hier bewusst ein einfaches, günstiges Polling statt eines echten
|
||||
* Reaktivitäts-Mechanismus.
|
||||
*/
|
||||
export function useScanActivity(): ScanActivity {
|
||||
const [activity, setActivity] = useState<ScanActivity>(() => computeActivity());
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => setActivity(computeActivity()), 1500);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return activity;
|
||||
}
|
||||
@@ -20,8 +20,34 @@ import {
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { StatusBadge } from "@launchpad/ui";
|
||||
import { useBackendHealth } from "../../hooks/useBackendHealth.js";
|
||||
import { useScanActivity } from "../../hooks/useScanActivity.js";
|
||||
import { ScrollToTopButton } from "../../components/ScrollToTopButton.js";
|
||||
|
||||
function ScanActivityBadge() {
|
||||
const { running, pendingCount } = useScanActivity();
|
||||
|
||||
if (running) {
|
||||
return (
|
||||
<span
|
||||
title="Ein Scan läuft gerade"
|
||||
className="ml-auto h-1.5 w-1.5 shrink-0 animate-pulse rounded-full bg-blue-500"
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (pendingCount > 0) {
|
||||
return (
|
||||
<span
|
||||
title={`${pendingCount} Scan-Ergebnis(se) warten auf Bestätigung`}
|
||||
className="ml-auto flex h-4 min-w-4 shrink-0 items-center justify-center rounded-full
|
||||
bg-amber-500 px-1 text-[10px] font-medium text-white"
|
||||
>
|
||||
{pendingCount}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const NAV_ITEMS: { to: string; label: string; icon: IconDefinition }[] = [
|
||||
{ to: "/", label: "Startseite", icon: faHouse },
|
||||
{ to: "/admin/dashboard", label: "Dashboard", icon: faChartLine },
|
||||
@@ -124,6 +150,7 @@ export function AdminLayout() {
|
||||
<FontAwesomeIcon icon={item.icon} />
|
||||
</span>
|
||||
{item.label}
|
||||
{item.to === "/admin/scanner" ? <ScanActivityBadge /> : null}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -422,8 +422,8 @@ export function SettingsPage() {
|
||||
<div className="rounded-2xl border border-black/10 p-5 dark:border-white/10">
|
||||
<h2 className="mb-2 font-medium text-black dark:text-white">Scan</h2>
|
||||
<LimitSetting
|
||||
label="Nicht mehr gemeldete Geräte anzeigen nach (Tagen)"
|
||||
hint="Ein Gerät, das die FritzBox nicht mehr meldet, wird erst nach so vielen Tagen im Scan-Ergebnis zur Durchsicht vorgeschlagen. 0 = sofort beim ersten verpassten Scan."
|
||||
label="Nicht mehr gemeldete Geräte/Dienste anzeigen nach (Tagen)"
|
||||
hint="Gilt für Geräte, die die FritzBox nicht mehr meldet, UND für einzelne Dienste, deren Port bei einem Scan nicht mehr offen ist. 0 = sofort beim ersten verpassten Scan."
|
||||
settingKey="staleDeviceThresholdDays"
|
||||
value={settings?.staleDeviceThresholdDays}
|
||||
max={90}
|
||||
|
||||
Reference in New Issue
Block a user