generated from Dicken/dickendock
round30: alle drei Scanner als Hintergrund-Job, Dienst-Duplikate strukturell verhindert (Migration+Constraint+Geraete-Sperre), zweiter Erkennungsversuch bei leerem Ersttreffer
This commit is contained in:
@@ -18,22 +18,6 @@ interface DeviceChange {
|
||||
suggested: string;
|
||||
}
|
||||
|
||||
interface FritzBoxScanResult {
|
||||
found: number;
|
||||
uniqueDevices: number;
|
||||
staleDevices: Device[];
|
||||
deviceChanges: DeviceChange[];
|
||||
}
|
||||
|
||||
async function scanFritzBox(): Promise<FritzBoxScanResult> {
|
||||
const res = await fetch("/api/scan/fritzbox", { method: "POST" });
|
||||
const body = await res.json();
|
||||
if (!res.ok) {
|
||||
throw new Error(body.error ?? `FritzBox-Scan fehlgeschlagen (HTTP ${res.status})`);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
async function patchDevice(id: string, patch: Record<string, unknown>) {
|
||||
const res = await fetch(`/api/devices/${id}`, {
|
||||
method: "PATCH",
|
||||
@@ -441,18 +425,56 @@ export function ScannerPage() {
|
||||
null
|
||||
);
|
||||
|
||||
const fritzboxMutation = useMutation({
|
||||
mutationFn: scanFritzBox,
|
||||
onSuccess: (result) => {
|
||||
setFritzboxSummary({ found: result.found, uniqueDevices: result.uniqueDevices });
|
||||
setFritzboxError(null);
|
||||
setStaleDevices(result.staleDevices);
|
||||
setDeviceChanges(result.deviceChanges);
|
||||
queryClient.invalidateQueries({ queryKey: ["devices"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["logs"] });
|
||||
},
|
||||
onError: (error: Error) => setFritzboxError(error.message),
|
||||
});
|
||||
const [fritzboxRunning, setFritzboxRunning] = useState(false);
|
||||
const lastFritzboxHandledAt = useRef<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
async function poll() {
|
||||
try {
|
||||
const res = await fetch("/api/scan/fritzbox/status");
|
||||
if (!res.ok || cancelled) return;
|
||||
const s = await res.json();
|
||||
|
||||
setFritzboxRunning(s.running);
|
||||
|
||||
if (!s.running && s.lastResult && s.startedAt !== lastFritzboxHandledAt.current) {
|
||||
lastFritzboxHandledAt.current = s.startedAt;
|
||||
setFritzboxSummary({ found: s.lastResult.found, uniqueDevices: s.lastResult.uniqueDevices });
|
||||
setFritzboxError(null);
|
||||
setStaleDevices(s.lastResult.staleDevices);
|
||||
setDeviceChanges(s.lastResult.deviceChanges);
|
||||
queryClient.invalidateQueries({ queryKey: ["devices"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["logs"] });
|
||||
} else if (!s.running && s.lastError && s.startedAt !== lastFritzboxHandledAt.current) {
|
||||
lastFritzboxHandledAt.current = s.startedAt;
|
||||
setFritzboxError(s.lastError);
|
||||
}
|
||||
} catch {
|
||||
// Netzwerk-Hänger beim Poll soll nicht die Seite stören.
|
||||
}
|
||||
}
|
||||
|
||||
poll();
|
||||
const interval = setInterval(poll, 1500);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
clearInterval(interval);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
async function startFritzboxScan() {
|
||||
setFritzboxError(null);
|
||||
const res = await fetch("/api/scan/fritzbox", { method: "POST" });
|
||||
if (!res.ok) {
|
||||
const body = await res.json().catch(() => ({}));
|
||||
setFritzboxError(body.error ?? `Fehlgeschlagen (HTTP ${res.status})`);
|
||||
return;
|
||||
}
|
||||
setFritzboxRunning(true);
|
||||
}
|
||||
|
||||
// Geräteübergreifender Sammel-Scan: läuft als Hintergrund-Job im Backend
|
||||
// (siehe apps/backend/src/scanJobs.ts), nicht mehr als Schleife aus
|
||||
@@ -548,10 +570,10 @@ export function ScannerPage() {
|
||||
<Button
|
||||
variant="primary"
|
||||
className="mt-4"
|
||||
onClick={() => fritzboxMutation.mutate()}
|
||||
disabled={fritzboxMutation.isPending}
|
||||
onClick={startFritzboxScan}
|
||||
disabled={fritzboxRunning}
|
||||
>
|
||||
{fritzboxMutation.isPending ? "Scanne …" : "FritzBox jetzt scannen"}
|
||||
{fritzboxRunning ? "Scanne …" : "FritzBox jetzt scannen"}
|
||||
</Button>
|
||||
{fritzboxSummary ? (
|
||||
<p className="mt-2 text-sm text-emerald-600 dark:text-emerald-400">
|
||||
|
||||
Reference in New Issue
Block a user