Commit 6: Adminbereich (TanStack Router, 8 Menüpunkte, Scan-Logs)

This commit is contained in:
2026-07-19 02:53:04 +02:00
parent b0ff551ca0
commit 2f31996bbb
31 changed files with 1693 additions and 112 deletions

View File

@@ -1,17 +1,17 @@
import type { FastifyInstance } from "fastify";
import * as deviceRepo from "../db/repositories/devices.js";
import * as serviceRepo from "../db/repositories/services.js";
import * as logRepo from "../db/repositories/logs.js";
import { scanDeviceServices } from "../scanner/networkScanner.js";
import { fetchFritzBoxHosts } from "../scanner/fritzbox.js";
/**
* Scan-Endpunkte. Werden ausschließlich manuell per Knopfdruck ("Jetzt
* scannen") aus der Admin-UI (Commit 6) ausgelöst es gibt keinerlei
* automatischen/zeitgesteuerten Scan.
* scannen") aus der Admin-UI ausgelöst es gibt keinerlei automatischen/
* zeitgesteuerten Scan. Jeder Versuch (Erfolg oder Fehler) wird in scan_logs
* protokolliert und ist unter Admin -> Logs einsehbar.
*/
export async function scanRoutes(app: FastifyInstance): Promise<void> {
// Netzwerk-Scan für ein einzelnes, bereits bekanntes Gerät: DNS-Kandidaten,
// Port 80/443 + typische Ports, Titel/Favicon, Softwareerkennung.
app.post("/api/scan/devices/:id", async (request, reply) => {
const { id } = request.params as { id: string };
const device = deviceRepo.getDevice(id);
@@ -19,39 +19,61 @@ export async function scanRoutes(app: FastifyInstance): Promise<void> {
return reply.code(404).send({ error: "Gerät nicht gefunden" });
}
const discovered = await scanDeviceServices(device);
try {
const discovered = await scanDeviceServices(device);
const results = discovered.map((found) =>
serviceRepo.upsertServiceFromScan({
const results = discovered.map((found) =>
serviceRepo.upsertServiceFromScan({
deviceId: device.id,
hostname: found.hostname,
url: found.url,
https: found.https,
port: found.port,
favicon: found.favicon,
description: found.description,
suggestedDisplayName: found.suggestedDisplayName,
suggestedCategory: found.category,
})
);
deviceRepo.upsertDeviceFromScan({
hostname: device.hostname,
ip: device.ip,
mac: device.mac,
manufacturer: device.manufacturer,
model: device.model,
online: discovered.length > 0,
source: device.source,
});
const created = results.filter((r) => r.created).length;
const updated = results.filter((r) => !r.created).length;
logRepo.logScan({
type: "device",
targetId: device.id,
level: "info",
message: `${device.hostname} (${device.ip}): ${discovered.length} Dienst(e) gefunden, ${created} neu, ${updated} aktualisiert`,
});
return {
deviceId: device.id,
hostname: found.hostname,
url: found.url,
https: found.https,
port: found.port,
favicon: found.favicon,
description: found.description,
suggestedDisplayName: found.suggestedDisplayName,
suggestedCategory: found.category,
})
);
deviceRepo.upsertDeviceFromScan({
hostname: device.hostname,
ip: device.ip,
mac: device.mac,
manufacturer: device.manufacturer,
model: device.model,
online: discovered.length > 0,
source: device.source,
});
return {
deviceId: device.id,
scannedPorts: discovered.length,
created: results.filter((r) => r.created).length,
updated: results.filter((r) => !r.created).length,
services: results.map((r) => r.service),
};
scannedPorts: discovered.length,
created,
updated,
services: results.map((r) => r.service),
};
} catch (err) {
const detail = err instanceof Error ? err.message : String(err);
logRepo.logScan({
type: "device",
targetId: device.id,
level: "error",
message: `${device.hostname} (${device.ip}): Scan fehlgeschlagen ${detail}`,
});
request.log.error(err);
return reply.code(500).send({ error: "Geräte-Scan fehlgeschlagen", detail });
}
});
// FritzBox-Scan: liest die Geräteliste per TR-064 und legt/aktualisiert Geräte.
@@ -82,13 +104,23 @@ export async function scanRoutes(app: FastifyInstance): Promise<void> {
source: "fritzbox",
})
);
logRepo.logScan({
type: "fritzbox",
level: "info",
message: `FritzBox-Scan: ${hosts.length} Gerät(e) gefunden`,
});
return { found: hosts.length, devices };
} catch (err) {
request.log.error(err);
return reply.code(502).send({
error: "FritzBox-Scan fehlgeschlagen",
detail: err instanceof Error ? err.message : String(err),
const detail = err instanceof Error ? err.message : String(err);
logRepo.logScan({
type: "fritzbox",
level: "error",
message: `FritzBox-Scan fehlgeschlagen ${detail}`,
});
request.log.error(err);
return reply.code(502).send({ error: "FritzBox-Scan fehlgeschlagen", detail });
}
});
}