generated from Dicken/dickendock
Fix: FritzBox faelschlich als Home Assistant erkannt + Reverse-DNS + Namens-Aenderungsvorschlaege beim Scan
This commit is contained in:
@@ -33,13 +33,99 @@ async function deleteServiceRequest(id: string) {
|
||||
}
|
||||
}
|
||||
|
||||
interface ScanNameChange {
|
||||
serviceId: string;
|
||||
serviceHostname: string;
|
||||
servicePort: number;
|
||||
field: "displayName" | "category";
|
||||
current: string | null;
|
||||
suggested: string;
|
||||
}
|
||||
|
||||
async function patchService(id: string, patch: Record<string, unknown>) {
|
||||
const res = await fetch(`/api/services/${id}`, {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(patch),
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error(`Dienst konnte nicht aktualisiert werden (HTTP ${res.status})`);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async function scanDeviceById(id: string) {
|
||||
const res = await fetch(`/api/scan/devices/${id}`, { method: "POST" });
|
||||
const body = await res.json();
|
||||
if (!res.ok) {
|
||||
throw new Error(body.detail ?? body.error ?? `Scan fehlgeschlagen (HTTP ${res.status})`);
|
||||
}
|
||||
return body as { created: number; updated: number; staleServices: Service[] };
|
||||
return body as {
|
||||
created: number;
|
||||
updated: number;
|
||||
staleServices: Service[];
|
||||
nameChanges: ScanNameChange[];
|
||||
deviceNameSuggestion: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
function NameChangesReview({
|
||||
nameChanges,
|
||||
onDone,
|
||||
}: {
|
||||
nameChanges: ScanNameChange[];
|
||||
onDone: () => void;
|
||||
}) {
|
||||
const queryClient = useQueryClient();
|
||||
const [handled, setHandled] = useState<Set<string>>(new Set());
|
||||
|
||||
const applyMutation = useMutation({
|
||||
mutationFn: (change: ScanNameChange) =>
|
||||
patchService(change.serviceId, { [change.field]: change.suggested }),
|
||||
onSuccess: (_data, change) => {
|
||||
setHandled((prev) => new Set(prev).add(`${change.serviceId}:${change.field}`));
|
||||
queryClient.invalidateQueries({ queryKey: ["services"] });
|
||||
},
|
||||
});
|
||||
|
||||
const remaining = nameChanges.filter((c) => !handled.has(`${c.serviceId}:${c.field}`));
|
||||
if (remaining.length === 0) return null;
|
||||
|
||||
const fieldLabel = (field: ScanNameChange["field"]) => (field === "displayName" ? "Name" : "Kategorie");
|
||||
|
||||
return (
|
||||
<div className="mt-3 rounded-xl border border-blue-500/30 bg-blue-500/5 p-3 text-xs">
|
||||
<p className="mb-2 font-medium text-blue-700 dark:text-blue-400">
|
||||
{remaining.length} erkannte Änderung(en) über alle gescannten Geräte hinweg weichen vom
|
||||
gespeicherten Wert ab:
|
||||
</p>
|
||||
<ul className="max-h-60 space-y-1 overflow-y-auto">
|
||||
{remaining.map((c) => (
|
||||
<li key={`${c.serviceId}:${c.field}`} className="flex items-center justify-between gap-2">
|
||||
<span className="text-black/70 dark:text-white/70">
|
||||
{c.serviceHostname}:{c.servicePort} – {fieldLabel(c.field)}: „{c.current ?? "–"}" → „
|
||||
{c.suggested}"
|
||||
</span>
|
||||
<div className="flex gap-1">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => setHandled((prev) => new Set(prev).add(`${c.serviceId}:${c.field}`))}
|
||||
>
|
||||
Behalten
|
||||
</Button>
|
||||
<Button size="sm" variant="primary" onClick={() => applyMutation.mutate(c)} disabled={applyMutation.isPending}>
|
||||
Übernehmen
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<button onClick={onDone} className="mt-2 text-black/40 underline dark:text-white/40">
|
||||
Hinweis schließen
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StaleServicesReview({
|
||||
@@ -154,6 +240,7 @@ export function ScannerPage() {
|
||||
const [bulkStatus, setBulkStatus] = useState<string | null>(null);
|
||||
const [bulkRunning, setBulkRunning] = useState(false);
|
||||
const [bulkStaleServices, setBulkStaleServices] = useState<Service[]>([]);
|
||||
const [bulkNameChanges, setBulkNameChanges] = useState<ScanNameChange[]>([]);
|
||||
const [staleDevices, setStaleDevices] = useState<Device[]>([]);
|
||||
|
||||
const fritzboxMutation = useMutation({
|
||||
@@ -170,9 +257,11 @@ export function ScannerPage() {
|
||||
if (!devices || devices.length === 0) return;
|
||||
setBulkRunning(true);
|
||||
setBulkStaleServices([]);
|
||||
setBulkNameChanges([]);
|
||||
let created = 0;
|
||||
let updated = 0;
|
||||
const allStale: Service[] = [];
|
||||
const allNameChanges: ScanNameChange[] = [];
|
||||
|
||||
for (const device of devices) {
|
||||
try {
|
||||
@@ -180,6 +269,7 @@ export function ScannerPage() {
|
||||
created += result.created;
|
||||
updated += result.updated;
|
||||
allStale.push(...result.staleServices);
|
||||
allNameChanges.push(...result.nameChanges);
|
||||
setBulkStatus(`Scanne ${device.hostname} … (${created} neu, ${updated} aktualisiert bisher)`);
|
||||
} catch {
|
||||
// einzelnes fehlgeschlagenes Gerät soll den Rest nicht abbrechen
|
||||
@@ -188,10 +278,11 @@ export function ScannerPage() {
|
||||
|
||||
setBulkStatus(
|
||||
`Fertig: ${devices.length} Gerät(e) gescannt, ${created} neue Dienste, ${updated} aktualisiert${
|
||||
allStale.length > 0 ? `, ${allStale.length} nicht mehr gefunden (siehe unten)` : ""
|
||||
}.`
|
||||
allStale.length > 0 ? `, ${allStale.length} nicht mehr gefunden` : ""
|
||||
}${allNameChanges.length > 0 ? `, ${allNameChanges.length} Namensänderung(en) vorgeschlagen` : ""} (siehe unten).`
|
||||
);
|
||||
setBulkStaleServices(allStale);
|
||||
setBulkNameChanges(allNameChanges);
|
||||
setBulkRunning(false);
|
||||
queryClient.invalidateQueries({ queryKey: ["devices"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["services"] });
|
||||
@@ -254,6 +345,9 @@ export function ScannerPage() {
|
||||
{bulkStatus ? (
|
||||
<p className="mt-2 text-sm text-black/50 dark:text-white/50">{bulkStatus}</p>
|
||||
) : null}
|
||||
{bulkNameChanges.length > 0 ? (
|
||||
<NameChangesReview nameChanges={bulkNameChanges} onDone={() => setBulkNameChanges([])} />
|
||||
) : null}
|
||||
{bulkStaleServices.length > 0 ? (
|
||||
<StaleServicesReview
|
||||
staleServices={bulkStaleServices}
|
||||
|
||||
Reference in New Issue
Block a user