generated from Dicken/dickendock
round60: Delta-Vergleich zum vorherigen Scan (FritzBox + Sammel-Scan) statt statischer Zahlen
This commit is contained in:
@@ -334,6 +334,10 @@ export async function scanRoutes(app: FastifyInstance): Promise<void> {
|
|||||||
const allStale: DeviceScanResult["staleServices"] = [];
|
const allStale: DeviceScanResult["staleServices"] = [];
|
||||||
const allNameChanges: DeviceScanResult["nameChanges"] = [];
|
const allNameChanges: DeviceScanResult["nameChanges"] = [];
|
||||||
const allNew: DeviceScanResult["newServices"] = [];
|
const allNew: DeviceScanResult["newServices"] = [];
|
||||||
|
// Sammelt die IDs ALLER gefundenen Dienste über den kompletten
|
||||||
|
// Sammel-Scan hinweg - für den Vergleich mit dem vorherigen Lauf (siehe
|
||||||
|
// weiter unten bei finishJob).
|
||||||
|
const allServiceIds: string[] = [];
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -536,6 +540,7 @@ export async function scanRoutes(app: FastifyInstance): Promise<void> {
|
|||||||
allStale.push(...result.staleServices);
|
allStale.push(...result.staleServices);
|
||||||
allNameChanges.push(...result.nameChanges);
|
allNameChanges.push(...result.nameChanges);
|
||||||
allNew.push(...result.newServices);
|
allNew.push(...result.newServices);
|
||||||
|
allServiceIds.push(...result.services.map((s) => s.id));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,6 +571,7 @@ export async function scanRoutes(app: FastifyInstance): Promise<void> {
|
|||||||
allStale.push(...result.staleServices);
|
allStale.push(...result.staleServices);
|
||||||
allNameChanges.push(...result.nameChanges);
|
allNameChanges.push(...result.nameChanges);
|
||||||
allNew.push(...result.newServices);
|
allNew.push(...result.newServices);
|
||||||
|
allServiceIds.push(...result.services.map((s) => s.id));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Einzelnes fehlgeschlagenes Gerät soll den Rest nicht
|
// Einzelnes fehlgeschlagenes Gerät soll den Rest nicht
|
||||||
// abbrechen - ABER trotzdem persistent loggen, statt
|
// abbrechen - ABER trotzdem persistent loggen, statt
|
||||||
@@ -598,6 +604,38 @@ export async function scanRoutes(app: FastifyInstance): Promise<void> {
|
|||||||
updateJobProgress("devices", scannedCount, null);
|
updateJobProgress("devices", scannedCount, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEU (round60): Vergleich mit dem VORHERIGEN Sammel-Scan (siehe
|
||||||
|
// gleiches Prinzip beim FritzBox-Scan oben) - Mengenvergleich der
|
||||||
|
// gefundenen Dienst-IDs statt reiner Zahlenvergleich.
|
||||||
|
const previousServiceIdsRaw = settingsRepo.getSetting("deviceScanLastServiceIds");
|
||||||
|
const previousServiceIds: string[] | null = previousServiceIdsRaw ? JSON.parse(previousServiceIdsRaw) : null;
|
||||||
|
const currentServiceIdSet = new Set(allServiceIds);
|
||||||
|
|
||||||
|
let changesSinceLastScan: string;
|
||||||
|
if (cancelled) {
|
||||||
|
changesSinceLastScan = "Abgebrochen - kein vollständiger Vergleich möglich.";
|
||||||
|
} else if (previousServiceIds === null) {
|
||||||
|
changesSinceLastScan = "Erster Scan - kein Vergleich zu einem vorherigen Lauf möglich.";
|
||||||
|
} else {
|
||||||
|
const previousSet = new Set(previousServiceIds);
|
||||||
|
const newlyAppeared = allServiceIds.filter((id) => !previousSet.has(id));
|
||||||
|
const noLongerSeen = previousServiceIds.filter((id) => !currentServiceIdSet.has(id));
|
||||||
|
if (newlyAppeared.length === 0 && noLongerSeen.length === 0) {
|
||||||
|
changesSinceLastScan = "Keine Änderungen seit dem letzten Sammel-Scan.";
|
||||||
|
} else {
|
||||||
|
const parts: string[] = [];
|
||||||
|
if (newlyAppeared.length > 0) parts.push(`${newlyAppeared.length} neue(r) Dienst(e)`);
|
||||||
|
if (noLongerSeen.length > 0) parts.push(`${noLongerSeen.length} Dienst(e) fehlen jetzt`);
|
||||||
|
changesSinceLastScan = `Änderungen seit dem letzten Sammel-Scan: ${parts.join(", ")}.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Bei einem abgebrochenen Lauf NICHT als Vergleichsbasis speichern -
|
||||||
|
// sonst würde ein unvollständiger Durchlauf fälschlich "Dienste
|
||||||
|
// fehlen jetzt" für alle noch gar nicht gescannten Geräte anzeigen.
|
||||||
|
if (!cancelled) {
|
||||||
|
settingsRepo.setSetting("deviceScanLastServiceIds", JSON.stringify(allServiceIds));
|
||||||
|
}
|
||||||
|
|
||||||
finishJob("devices", {
|
finishJob("devices", {
|
||||||
totalDevices: devices.length,
|
totalDevices: devices.length,
|
||||||
scannedCount,
|
scannedCount,
|
||||||
@@ -607,6 +645,7 @@ export async function scanRoutes(app: FastifyInstance): Promise<void> {
|
|||||||
staleServices: allStale,
|
staleServices: allStale,
|
||||||
nameChanges: allNameChanges,
|
nameChanges: allNameChanges,
|
||||||
newServices: allNew,
|
newServices: allNew,
|
||||||
|
changesSinceLastScan,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// WICHTIG: Vorher wurde ein Abbruch des GESAMTEN Sammel-Scans nur im
|
// WICHTIG: Vorher wurde ein Abbruch des GESAMTEN Sammel-Scans nur im
|
||||||
@@ -794,10 +833,43 @@ function mergeFritzboxHosts(hosts: FritzBoxHost[]): FritzBoxHost[] {
|
|||||||
return now - lastSeen >= staleThresholdMs;
|
return now - lastSeen >= staleThresholdMs;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// NEU (round60): Vergleich mit dem VORHERIGEN Scan-Lauf - vorher
|
||||||
|
// stand hier bei jedem Scan nur eine absolute Zahl ("45 Einträge"),
|
||||||
|
// egal ob sich seit dem letzten Mal etwas geändert hatte oder nicht
|
||||||
|
// (siehe Bugreport: "wird immer nur angezeigt wieviel Einträge beim
|
||||||
|
// letzten Scan waren, das die ganze Zeit"). Die Geräte-IDs des
|
||||||
|
// letzten erfolgreichen Laufs werden dafür in den Einstellungen
|
||||||
|
// (app_settings) gemerkt und mit den JETZT gefundenen verglichen -
|
||||||
|
// ein einfacher Mengenvergleich (nicht nur ein Zahlenvergleich),
|
||||||
|
// damit "3 raus, 3 rein" nicht fälschlich als "keine Änderung"
|
||||||
|
// durchgeht.
|
||||||
|
const previousDeviceIdsRaw = settingsRepo.getSetting("fritzboxScanLastDeviceIds");
|
||||||
|
const previousDeviceIds: string[] = previousDeviceIdsRaw ? JSON.parse(previousDeviceIdsRaw) : null;
|
||||||
|
const currentDeviceIds = devices.map((d) => d.id);
|
||||||
|
const currentDeviceIdSet = new Set(currentDeviceIds);
|
||||||
|
|
||||||
|
let changesSinceLastScan: string;
|
||||||
|
if (previousDeviceIds === null) {
|
||||||
|
changesSinceLastScan = "Erster Scan - kein Vergleich zu einem vorherigen Lauf möglich.";
|
||||||
|
} else {
|
||||||
|
const previousSet = new Set(previousDeviceIds);
|
||||||
|
const newlyAppeared = currentDeviceIds.filter((id) => !previousSet.has(id));
|
||||||
|
const noLongerSeen = previousDeviceIds.filter((id) => !currentDeviceIdSet.has(id));
|
||||||
|
if (newlyAppeared.length === 0 && noLongerSeen.length === 0) {
|
||||||
|
changesSinceLastScan = "Keine Änderungen seit dem letzten Scan.";
|
||||||
|
} else {
|
||||||
|
const parts: string[] = [];
|
||||||
|
if (newlyAppeared.length > 0) parts.push(`${newlyAppeared.length} neue(s) Gerät(e)`);
|
||||||
|
if (noLongerSeen.length > 0) parts.push(`${noLongerSeen.length} Gerät(e) fehlen jetzt`);
|
||||||
|
changesSinceLastScan = `Änderungen seit dem letzten Scan: ${parts.join(", ")}.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settingsRepo.setSetting("fritzboxScanLastDeviceIds", JSON.stringify(currentDeviceIds));
|
||||||
|
|
||||||
logRepo.logScan({
|
logRepo.logScan({
|
||||||
type: "fritzbox",
|
type: "fritzbox",
|
||||||
level: "info",
|
level: "info",
|
||||||
message: `FritzBox-Scan: ${hosts.length} Eintrag/Einträge (${devices.length} eindeutige Geräte)${staleDevices.length > 0 ? `, ${staleDevices.length} nicht mehr gemeldet` : ""}${deviceChanges.length > 0 ? `, ${deviceChanges.length} Änderung(en) vorgeschlagen` : ""}`,
|
message: `FritzBox-Scan: ${hosts.length} Eintrag/Einträge (${devices.length} eindeutige Geräte)${staleDevices.length > 0 ? `, ${staleDevices.length} nicht mehr gemeldet` : ""}${deviceChanges.length > 0 ? `, ${deviceChanges.length} Änderung(en) vorgeschlagen` : ""} - ${changesSinceLastScan}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
finishJob("fritzbox", {
|
finishJob("fritzbox", {
|
||||||
@@ -805,6 +877,7 @@ function mergeFritzboxHosts(hosts: FritzBoxHost[]): FritzBoxHost[] {
|
|||||||
uniqueDevices: devices.length,
|
uniqueDevices: devices.length,
|
||||||
staleDevices,
|
staleDevices,
|
||||||
deviceChanges,
|
deviceChanges,
|
||||||
|
changesSinceLastScan,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const detail = err instanceof Error ? err.message : String(err);
|
const detail = err instanceof Error ? err.message : String(err);
|
||||||
|
|||||||
@@ -551,7 +551,7 @@ export function ScannerPage() {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
const [fritzboxSummary, setFritzboxSummary] = usePersistedState<
|
const [fritzboxSummary, setFritzboxSummary] = usePersistedState<
|
||||||
{ found: number; uniqueDevices: number } | null
|
{ found: number; uniqueDevices: number; changesSinceLastScan?: string } | null
|
||||||
>("scanner:fritzboxSummary", null);
|
>("scanner:fritzboxSummary", null);
|
||||||
const [fritzboxError, setFritzboxError] = usePersistedState<string | null>(
|
const [fritzboxError, setFritzboxError] = usePersistedState<string | null>(
|
||||||
"scanner:fritzboxError",
|
"scanner:fritzboxError",
|
||||||
@@ -573,7 +573,11 @@ export function ScannerPage() {
|
|||||||
|
|
||||||
if (!s.running && s.lastResult && s.startedAt !== getHandledResultId("scanner:fritzboxLastHandledAt")) {
|
if (!s.running && s.lastResult && s.startedAt !== getHandledResultId("scanner:fritzboxLastHandledAt")) {
|
||||||
setHandledResultId("scanner:fritzboxLastHandledAt", s.startedAt);
|
setHandledResultId("scanner:fritzboxLastHandledAt", s.startedAt);
|
||||||
setFritzboxSummary({ found: s.lastResult.found, uniqueDevices: s.lastResult.uniqueDevices });
|
setFritzboxSummary({
|
||||||
|
found: s.lastResult.found,
|
||||||
|
uniqueDevices: s.lastResult.uniqueDevices,
|
||||||
|
changesSinceLastScan: s.lastResult.changesSinceLastScan,
|
||||||
|
});
|
||||||
setFritzboxError(null);
|
setFritzboxError(null);
|
||||||
setStaleDevices(s.lastResult.staleDevices);
|
setStaleDevices(s.lastResult.staleDevices);
|
||||||
setDeviceChanges(s.lastResult.deviceChanges);
|
setDeviceChanges(s.lastResult.deviceChanges);
|
||||||
@@ -650,7 +654,8 @@ export function ScannerPage() {
|
|||||||
`${r.created} neue(r) Dienst(e) gefunden.` +
|
`${r.created} neue(r) Dienst(e) gefunden.` +
|
||||||
(pending > 0
|
(pending > 0
|
||||||
? ` ${pending} Änderung(en) warten unten auf deine Bestätigung.`
|
? ` ${pending} Änderung(en) warten unten auf deine Bestätigung.`
|
||||||
: " Keine Änderungen an bestehenden Diensten vorgeschlagen.")
|
: " Keine Änderungen an bestehenden Diensten vorgeschlagen.") +
|
||||||
|
(r.changesSinceLastScan ? ` ${r.changesSinceLastScan}` : "")
|
||||||
);
|
);
|
||||||
setBulkStaleServices(r.staleServices);
|
setBulkStaleServices(r.staleServices);
|
||||||
setBulkNameChanges(r.nameChanges);
|
setBulkNameChanges(r.nameChanges);
|
||||||
@@ -730,14 +735,27 @@ export function ScannerPage() {
|
|||||||
{fritzboxRunning ? "Scanne …" : "FritzBox jetzt scannen"}
|
{fritzboxRunning ? "Scanne …" : "FritzBox jetzt scannen"}
|
||||||
</Button>
|
</Button>
|
||||||
{fritzboxSummary ? (
|
{fritzboxSummary ? (
|
||||||
<p className="mt-2 text-sm text-emerald-600 dark:text-emerald-400">
|
<div className="mt-2 text-sm">
|
||||||
{fritzboxSummary.found} Eintrag/Einträge von der FritzBox,{" "}
|
{fritzboxSummary.changesSinceLastScan ? (
|
||||||
{fritzboxSummary.uniqueDevices} eindeutige Geräte
|
<p
|
||||||
{fritzboxSummary.found !== fritzboxSummary.uniqueDevices
|
className={
|
||||||
? " (mehrere Einträge desselben Geräts wurden zusammengeführt)"
|
fritzboxSummary.changesSinceLastScan.startsWith("Keine Änderungen")
|
||||||
: ""}
|
? "text-black/50 dark:text-white/50"
|
||||||
.
|
: "font-medium text-emerald-600 dark:text-emerald-400"
|
||||||
</p>
|
}
|
||||||
|
>
|
||||||
|
{fritzboxSummary.changesSinceLastScan}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
<p className="text-black/50 dark:text-white/50">
|
||||||
|
{fritzboxSummary.found} Eintrag/Einträge von der FritzBox,{" "}
|
||||||
|
{fritzboxSummary.uniqueDevices} eindeutige Geräte
|
||||||
|
{fritzboxSummary.found !== fritzboxSummary.uniqueDevices
|
||||||
|
? " (mehrere Einträge desselben Geräts wurden zusammengeführt)"
|
||||||
|
: ""}
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{fritzboxError ? <p className="mt-2 text-sm text-red-500">{fritzboxError}</p> : null}
|
{fritzboxError ? <p className="mt-2 text-sm text-red-500">{fritzboxError}</p> : null}
|
||||||
<DeviceChangesReview
|
<DeviceChangesReview
|
||||||
|
|||||||
Reference in New Issue
Block a user