generated from Dicken/dickendock
235 lines
13 KiB
Markdown
235 lines
13 KiB
Markdown
# Roadmap
|
||
|
||
Geplante Reihenfolge der nächsten Commits, aufbauend auf dem lauffähigen
|
||
Grundgerüst aus Commit 1.
|
||
|
||
## ✅ Commit 2 — Geräte- & Dienste-API (erledigt)
|
||
|
||
- REST-Endpunkte (Fastify) für `GET/POST/PATCH/DELETE` auf `devices` und `services`
|
||
- Zod-Validierung der Request-Bodies (`packages/shared/src/schemas.ts`)
|
||
- Repository-Layer über Drizzle (`apps/backend/src/db/repositories`)
|
||
|
||
## ✅ Commit 3 — Suche im Frontend (erledigt)
|
||
|
||
- Anbindung von `rankServices` aus `packages/shared` an echte Backend-Daten
|
||
- TanStack Query für Datenhaltung (`apps/frontend/src/hooks/useServices.ts`)
|
||
- Tastatur-Navigation (Pfeiltasten wählen, Enter öffnet Dienst in neuem Tab, Escape leert Suche)
|
||
- Neue UI-Komponente `ResultsList` in `packages/ui`
|
||
|
||
> TanStack Router wird bewusst erst mit dem Adminbereich (Commit 6) eingeführt,
|
||
> da es vorher keine zweite Route gibt, die er sinnvoll verwalten könnte.
|
||
|
||
## ✅ Commit 4 — Kategorien & Favoriten (erledigt)
|
||
|
||
- Kategorien-API: Erstellen, Umbenennen, Löschen, Bulk-Reorder (`PATCH /api/categories/reorder`)
|
||
- Löschen einer Kategorie setzt `category` bei betroffenen Diensten auf `null`,
|
||
löscht die Dienste aber nicht
|
||
- Favoriten-Toggle direkt in der Trefferliste des Frontends (Stern anklicken,
|
||
per `useMutation` + Cache-Invalidierung)
|
||
|
||
> Die eigentliche Kategorien-*Verwaltungsoberfläche* (Erstellen/Umbenennen/
|
||
> Drag & Drop in der UI) wandert in Commit 6 (Adminbereich) – die Startseite
|
||
> bleibt bewusst die minimalistische Suche, kein Verwaltungs-UI dort.
|
||
|
||
## ✅ Commit 5 — Scanner (erledigt)
|
||
|
||
- `apps/backend/src/scanner/`: DNS-Kandidaten-Auflösung, TCP-Portscan (80, 443 +
|
||
typische Ports), HTTP-Titel-/Favicon-Extraktion, Softwareerkennung per
|
||
Signatur-Liste, FritzBox-TR-064-Client (inkl. selbst implementierter
|
||
HTTP-Digest-Authentifizierung)
|
||
- `POST /api/scan/devices/:id` und `POST /api/scan/fritzbox` – ausschließlich
|
||
manuell auslösbar, kein automatischer/zeitgesteuerter Scan
|
||
- `upsertDeviceFromScan` / `upsertServiceFromScan` in den Repositories:
|
||
garantiert, dass Benutzerfelder (displayName, category, favorite, order,
|
||
alias, icon) bei erneuten Scans nie überschrieben werden – End-to-End getestet
|
||
(Service manuell umbenannt/kategorisiert/favorisiert, erneut gescannt,
|
||
Werte blieben erhalten)
|
||
|
||
> Alle Module wurden gegen echte, lokal gestartete Test-Server verifiziert
|
||
> (Port-Erkennung, Titel/Favicon-Parsing, Softwareerkennung, kompletter
|
||
> FritzBox-SOAP-/Digest-Auth-Ablauf inkl. Fehlerfall bei falschem Passwort).
|
||
> Ein Test gegen eine echte FritzBox war in dieser Umgebung nicht möglich.
|
||
|
||
## ✅ Commit 6 — Adminbereich (erledigt)
|
||
|
||
- TanStack Router (code-basiert): `/` (Startseite) + `/admin/*`
|
||
- Menüpunkte wie spezifiziert: Dashboard, Geräte, Dienste, Scanner, Kategorien,
|
||
Plugins, Einstellungen, Logs
|
||
- Geräte-Seite: anlegen, löschen, **„Jetzt scannen"-Button** pro Gerät
|
||
- Dienste-Seite: Inline-Bearbeitung von Name/Kategorie/Alias/Reihenfolge, Favoriten-Toggle
|
||
- Kategorien-Seite: anlegen, umbenennen, löschen, **natives Drag & Drop** (kein
|
||
zusätzliches Package nötig)
|
||
- Scanner-Seite: FritzBox-Trigger + Sammel-Scan aller Geräte
|
||
- Logs-Seite: Scan-Historie (neue `scan_logs`-Tabelle, jeder Scan-Versuch wird protokolliert)
|
||
- Einstellungen: Live-Backend-Status + Theme-Umschalter
|
||
- Plugins: ehrlicher Hinweis, dass das Plugin-System noch nicht existiert
|
||
(statt einer vorgetäuschten Funktion)
|
||
|
||
> Verifiziert per Docker-Build-Simulation (Frontend + Backend inkl.
|
||
> `pnpm deploy --prod`) und Backend-Smoke-Test aller von den Admin-Seiten
|
||
> verwendeten Endpunkte. Die UI selbst konnte in dieser Umgebung nicht in
|
||
> einem echten Browser durchgeklickt werden (kein Browser-Tool verfügbar) –
|
||
> Verifikation stützt sich auf erfolgreichen TypeScript-/Vite-Build und
|
||
> Codeprüfung.
|
||
|
||
## ✅ Commit 7 — PWA (erledigt)
|
||
|
||
- `vite-plugin-pwa` mit generiertem Manifest (`manifest.webmanifest`) und
|
||
Service Worker (Workbox, `generateSW`-Modus)
|
||
- Echte Icons (192/512, jeweils normal + maskable, `apple-touch-icon.png`,
|
||
`favicon.svg`) unter `apps/frontend/public/`
|
||
- App-Shell wird precached; `/api/*` läuft über `NetworkFirst` mit 3s-Timeout
|
||
und Cache-Fallback – Suche funktioniert damit auch offline mit dem zuletzt
|
||
geladenen Datenstand
|
||
- `nginx.conf`: `sw.js`/`manifest.webmanifest` explizit von der 7-Tage-Cache-Regel
|
||
ausgenommen (sonst kommen Updates bei installierten Nutzern nie an), korrekter
|
||
MIME-Type für das Manifest gesetzt
|
||
|
||
> Verifiziert: Manifest gegen Installierbarkeits-Anforderungen geprüft (512er-Icon,
|
||
> maskable Icon, alle Pflichtfelder vorhanden), alle Assets über einen echten
|
||
> HTTP-Server abgerufen (200 OK, korrekte Content-Types), generierter Service-Worker-
|
||
> Code inspiziert (Precache-Liste + NetworkFirst-Route bestätigt), vollständige
|
||
> Docker-Build-Simulation durchlaufen. Die tatsächliche "Zum Homescreen hinzufügen"-
|
||
> Installation konnte mangels Browser in dieser Umgebung nicht getestet werden.
|
||
|
||
## ✅ Commit 8 — Plugin-System (erledigt)
|
||
|
||
- Plugins liegen als Ordner unter `apps/backend/plugins/*`, jeweils mit einer
|
||
`index.js` (reines ESM, kein Build-Schritt). Werden beim Backend-Start
|
||
geladen (`apps/backend/src/plugins/loader.ts`) und per Docker-Volume
|
||
eingebunden – neue Plugins brauchen nur einen Container-Neustart, kein
|
||
Image-Rebuild
|
||
- Plugin-Vertrag (`apps/backend/src/plugins/types.ts`): `setup(ctx)` zum
|
||
Registrieren zusätzlicher Softwareerkennung (inkl. Icon), `importDevices()`
|
||
für eigene Geräte-Importquellen
|
||
- Zwei funktionierende Beispiel-Plugins: `example-signatures` (Homebridge,
|
||
Uptime Kuma) und `static-import` (Geräte aus lokaler `devices.json`)
|
||
- `GET /api/plugins`, `POST /api/plugins/:name/import` – Import läuft wie
|
||
Scans ausschließlich manuell per Knopfdruck
|
||
- Frontend-Plugins-Seite zeigt echte geladene Plugins mit Capabilities und
|
||
Import-Button (ersetzt den ehrlichen Platzhalter aus Commit 6)
|
||
- Von neuen Plugins erkannte Software liefert ein Icon, das – wie `category`
|
||
seit Commit 5 – nur beim erstmaligen Anlegen eines Dienstes als Startwert
|
||
übernommen wird, nie nachträglich überschrieben
|
||
|
||
> Verifiziert: beide Plugins laden nachweislich beim Start; ein echter Scan
|
||
> gegen einen Test-Server mit "Homebridge" im Response-Body wurde über die
|
||
> Plugin-Signatur erkannt, inkl. korrekt übernommenem Icon; Geräte-Import per
|
||
> Plugin getestet (`source: "plugin"`); Fehlerfälle (unbekanntes Plugin → 404,
|
||
> Plugin ohne Import-Fähigkeit → 400) geprüft; Docker-Build-Simulation
|
||
> bestanden, dabei auch verifiziert, dass ein fehlendes Plugin-Verzeichnis
|
||
> nicht zum Absturz führt, sondern nur zu einer leeren Liste.
|
||
|
||
> **Bewusst nicht umgesetzt:** eigene Admin-Routen/Menüpunkte pro Plugin
|
||
> (alle Plugins erscheinen gesammelt auf einer Seite) und Sandboxing
|
||
> (Plugin-Code läuft mit vollem Zugriff im Backend-Prozess – nur Plugins aus
|
||
> vertrauenswürdiger Quelle einbinden, siehe `apps/backend/plugins/README.md`).
|
||
|
||
Damit ist die komplette in der ursprünglichen Projektübergabe beschriebene
|
||
Funktionalität umgesetzt.
|
||
|
||
## Bugfixes aus dem ersten Praxistest (nach Commit 8)
|
||
|
||
Basierend auf echtem Testen gegen ein reales Homelab (FritzBox + gescannte
|
||
Geräte):
|
||
|
||
- **Neues Feld `visible` auf Diensten.** Scan-Treffer mit Fehlerantworten
|
||
(404, 400, 302 als Titel) oder generischen Ladebildschirm-Titeln
|
||
("Loading...", der Scanner führt kein JavaScript aus) werden beim
|
||
erstmaligen Anlegen automatisch als `visible: false` markiert – sie
|
||
erscheinen nicht mehr in der Suche, bleiben aber unter Admin -> Dienste
|
||
sichtbar, umschaltbar (👁️/🙈) und löschbar. Bestehende Datenbanken werden
|
||
beim Start automatisch migriert (`ALTER TABLE ... ADD COLUMN`), keine
|
||
Daten gehen verloren.
|
||
- **Hostname/URL/HTTPS sind jetzt im Adminbereich editierbar** (Dienste ->
|
||
Bearbeiten) und werden ab sofort bei einem erneuten Scan eines bereits
|
||
bekannten Dienstes **nicht mehr überschrieben** (vorher wurden diese Felder
|
||
bei jedem Scan aufgefrischt). Nur `favicon`/`description` werden weiterhin
|
||
aktualisiert. Achtung: Wird dabei der **Port** geändert, entsteht beim
|
||
nächsten Scan ein neuer Eintrag für den ursprünglichen Port, da die
|
||
Zuordnung über `deviceId + port` läuft.
|
||
- **Suchergebnisliste erscheint nur noch bei aktiver Eingabe** – vorher war
|
||
sie auch bei leerem Suchfeld dauerhaft aufgeklappt.
|
||
- **Port wird jetzt überall mit angezeigt** (`hostname:port` statt nur
|
||
`hostname`), in der Suche wie im Adminbereich.
|
||
- **Admin-Sidebar ist jetzt echt fixiert** (`fixed` statt `static`) – vorher
|
||
scrollte "Zurück zur Suche" bei langem Seiteninhalt aus dem Sichtfeld.
|
||
|
||
## Weitere Fixes (zweite Testrunde)
|
||
|
||
- **Reset-Button** unter Admin -> Einstellungen -> Gefahrenzone: löscht alle
|
||
Geräte und (per Cascade) alle Dienste unwiderruflich. Doppelte Bestätigung
|
||
im Frontend (`window.confirm`) plus eine Pflicht-Bestätigung auf API-Ebene
|
||
(`POST /api/reset` verlangt `{"confirm": true}`, sonst `400`). Kategorien
|
||
und Logs bleiben erhalten.
|
||
- **Port und Protokoll (http/https) als eigene Spalten** in der
|
||
Dienste-Tabelle im Adminbereich, nicht mehr nur implizit in der URL.
|
||
- **Favoriten-Leiste über der Suchleiste** auf der Startseite – immer
|
||
sichtbar, unabhängig vom Suchfeld, sortiert nach der einstellbaren
|
||
Reihenfolge.
|
||
- **Dienste per Drag & Drop sortierbar** im Adminbereich (`PATCH
|
||
/api/services/reorder`), bestimmt sowohl die Reihenfolge in der
|
||
Favoriten-Leiste als auch bei gleichrangigen Suchtreffern.
|
||
- **Healthcheck durchgängig in der Admin-Sidebar sichtbar** (vorher nur auf
|
||
der Startseite und versteckt unter Einstellungen).
|
||
- **Kategorien-Synchronisierung:** Die `categories`-Tabelle war leer, obwohl
|
||
Dienste bereits Kategorien-Text trugen (z. B. aus der automatischen
|
||
Softwareerkennung) – `services.category` ist reiner Freitext, kein
|
||
Fremdschlüssel. Jetzt: (1) beim Start werden alle bereits auf Diensten
|
||
vorhandenen Kategorienamen automatisch in die `categories`-Tabelle
|
||
nachgetragen, (2) jeder Scan legt neu erkannte Kategorien automatisch an,
|
||
(3) Umbenennen einer Kategorie im Adminbereich aktualisiert automatisch
|
||
alle Dienste mit dem alten Namen. Einschränkung: manuell per API angelegte
|
||
Dienste (nicht über einen Scan) lösen diesen Sync aktuell nicht aus.
|
||
|
||
## HTTPS im lokalen Netz
|
||
|
||
- Frontend-Container liefert jetzt zusätzlich zu HTTP (Port 8080) auch HTTPS
|
||
auf Port 8443 aus, kein erzwungener Redirect.
|
||
- Entrypoint-Skript (`apps/frontend/docker-entrypoint.sh`) erzeugt beim
|
||
Containerstart automatisch ein selbstsigniertes Zertifikat für die in
|
||
`.env` hinterlegte `LAUNCHPAD_HOST` (IP-SAN bei IP-Adressen, DNS-SAN bei
|
||
Hostnamen) – übernimmt nichts, falls unter `/etc/nginx/certs/` bereits
|
||
eigene Zertifikate eingebunden sind (siehe README, z. B. per mkcert für
|
||
eine Variante ohne Browser-Warnung).
|
||
- `nginx.conf` in HTTP- und HTTPS-Serverblock aufgeteilt, gemeinsame
|
||
Location-Regeln in `nginx-locations.conf` ausgelagert (kein doppelt
|
||
gepflegter Proxy-/Cache-Code).
|
||
|
||
> Verifiziert: Zertifikatserzeugung für IP-, Hostname- und Fallback-Fall
|
||
> getestet, komplette nginx-Konfiguration mit einem echten nginx-Binary
|
||
> validiert (`nginx -t` erfolgreich), echter TLS-Handshake gegen den
|
||
> laufenden Server durchgeführt und geprüft, dass das ausgelieferte
|
||
> Zertifikat die korrekte IP als Subject Alternative Name trägt.
|
||
|
||
## HTTPS-Revision: eigene lokale Root-CA statt DuckDNS
|
||
|
||
Nutzer-Feedback: "Will nix externes, soll auch nur lokal laufen" – die
|
||
DuckDNS/Let's-Encrypt-Lösung wurde daher wieder entfernt und durch eine
|
||
komplett offline funktionierende Variante ersetzt.
|
||
|
||
- `apps/frontend/docker-entrypoint.sh` erzeugt beim ersten Containerstart
|
||
eine eigene Root-CA (`LaunchPad Local CA`, `CA:TRUE`, 10 Jahre gültig) und
|
||
signiert damit ein Server-Zertifikat (`CA:FALSE`, korrekte SAN je nach
|
||
`LAUNCHPAD_HOST`) – keinerlei externe Dienste beteiligt
|
||
- CA + Server-Zertifikat liegen im neuen Docker-Volume `launchpad-certs`
|
||
(statt im Container-Dateisystem) – bleiben dadurch über Neustarts und
|
||
`--build` stabil. Ändert sich nur `LAUNCHPAD_HOST`, wird ausschließlich das
|
||
Server-Zertifikat neu ausgestellt, die CA (und damit bereits erteiltes
|
||
Gerätevertrauen) bleibt unangetastet
|
||
- Root-CA wird unter `http://<host>:8080/ca.crt` zum Download bereitgestellt
|
||
(`nginx-locations.conf`, korrekter MIME-Type `application/x-x509-ca-cert`
|
||
löst auf Android/iOS automatisch den nativen "Zertifikat installieren"-Dialog
|
||
aus) – einziger verbleibender manueller Schritt ist der einmalige Import
|
||
pro Gerät, dafür gibt es ohne externe CA keine Alternative
|
||
- DuckDNS/`acme`-Service und zugehörige `.env`-Variablen vollständig entfernt
|
||
|
||
> Verifiziert: CA- und Leaf-Zertifikat-Erzeugung lokal mit echtem `openssl`
|
||
> durchgespielt (Zertifikatskette validiert erfolgreich gegen die CA,
|
||
> `CA:FALSE`/`CA:TRUE` korrekt gesetzt, SAN korrekt), Idempotenz geprüft
|
||
> (zweiter Lauf mit gleichem Host verändert nichts, geänderter Host erzeugt
|
||
> nur ein neues Server-Zertifikat, CA bleibt stabil), kompletter End-to-End-
|
||
> Test mit echtem nginx: `/ca.crt` per HTTP heruntergeladen und **damit ohne
|
||
> `-k`-Flag** eine echte HTTPS-Verbindung erfolgreich validiert – simuliert
|
||
> exakt das Verhalten eines Geräts nach CA-Import.
|