All checks were successful
Build APK / build-apk (push) Successful in 2m48s
Die vier letzten offenen Punkte der Roadmap. SIP-Erreichbarkeit (neues Werkzeug, Kategorie Telefonie) - Sip.kt: OPTIONS nach RFC 3261 ueber UDP, bei Schweigen TCP nachgeschoben. Antwortet nur TCP, ist das der Fingerabdruck einer Firewall, die UDP verwirft - eine andere Reparatur als "Anlage aus". - sip-bewertung.ts ohne Laufzeit-Import, damit tools/pruefe-sip.mjs die Regeln direkt nachrechnen kann (14 Faelle + 4 Texte, alle korrekt). - Leitsatz: JEDE Statuszeile beweist eine antwortende Instanz. 401/407 sind deshalb gruen mit Hinweis "Anmeldung verlangt" - beim Provider der Normalzustand. Als Ausfall gemeldet wuerde der Techniker am falschen Ende suchen. - RTP-Sprachqualitaet bewusst NICHT: ohne aufgebautes Gespraech nicht ehrlich messbar. Die Grenze steht im Ergebnis und landet im Kunden-PDF. Fingerabdruck-Anmeldung (Biometrie.kt, BiometricPrompt + Android-Keystore) - Der Kern ist nicht der Dialog, sondern was danach passiert: die Klartextfassung des Tokens wird aus den Capacitor Preferences ENTFERNT. Vorher stand es offen in CapacitorStorage.xml und war auf einem entsperrten Geraet ohne Weiteres lesbar - ein zusaetzlich verschluesseltes Token haette daran nichts geaendert. - Kein zweiter Faktor: der Finger ersetzt das Passwort (Eddys Vorgabe). Gueltigkeit und Widerruf bleiben bei awlauth - hinterlegt ist dasselbe Token. - 401 unterscheidet jetzt "Token abgelehnt" von "gar nicht angemeldet". Ohne das loeschte der erste Startaufruf die gerade eingerichtete Anmeldung wieder (im Emulator aufgefallen, Ursache im logcat). Token nicht mehr in der Adresszeile - updateDownloadUrl() ohne ?jwt=; der APK-Herunterlader im Plugin schickt Authorization: Bearer. instanceFollowRedirects=false, sonst ginge der Header bei einer Weiterleitung an ein fremdes Ziel mit. - pdfUrl() war seit dem Initialcommit tot (kein Aufrufer) und ist durch fetchProtocolPdf() ersetzt - Header statt Token in der URL. Im Emulator geprueft: SIP gegen einen eigenen Responder (200/401/Port zu), Fingerabdruck vollstaendig durchgespielt (einrichten, Neustart, anmelden, abbrechen), Speicher jeweils nachgesehen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
198 lines
10 KiB
Markdown
198 lines
10 KiB
Markdown
# NetDiag — Diagnose-App (Android)
|
|
|
|
Mobile Netzwerk-Diagnose-App. Erfasst vor Ort beim Kunden (Handy am WLAN oder
|
|
USB-C→RJ45-Adapter) Geräte, Ports und Messungen und hängt die Protokolle ans
|
|
Dolibarr-Modul `netdiag` an Kunde und Auftrag.
|
|
|
|
## Stack
|
|
|
|
SvelteKit 2 · Svelte 5 · Tailwind 4 · Vite 7 · Capacitor 6 · SQLite-Offline
|
|
|
|
## Entwicklung (Browser)
|
|
|
|
```bash
|
|
npm install
|
|
npm run dev # http://localhost:5175
|
|
```
|
|
|
|
Im Browser liefert ein **Mock** (`src/lib/scanner.ts`) Beispiel-Scandaten — die
|
|
Oberfläche lässt sich ohne Gerät entwickeln. API-Aufrufe gehen über den
|
|
Vite-Proxy an den Dolibarr-Testserver (`192.168.155.11`, siehe `vite.config.ts`).
|
|
|
|
## Android-Build
|
|
|
|
```bash
|
|
npm run build
|
|
npx cap add android # einmalig
|
|
# android/ liegt komplett im Repo — cap sync kopiert nur die Web-Assets
|
|
npx cap sync android
|
|
npx cap open android
|
|
```
|
|
|
|
Release-APK über CI: Commit mit `[apk]` in der Message → Forgejo baut und lädt
|
|
die APK in die Package Registry (`netdiag-apk`). Siehe `.forgejo/workflows/build.yml`.
|
|
|
|
## Architektur
|
|
|
|
```
|
|
src/lib/
|
|
api.ts JSON-API-Client (JWT, 401-Refresh, Timeout)
|
|
auth.svelte.ts Anmelde-Status
|
|
db.ts Offline-Speicher (SQLite nativ / localStorage Browser)
|
|
sync.svelte.ts Sync-Queue -> Dolibarr (idempotent über clientUuid)
|
|
scanner.ts Brücke zum nativen Plugin (+ Browser-Mock)
|
|
protocols.ts Protokoll-Operationen (addMeasurement, upsertDevice, …)
|
|
messfelder.ts Beschriftung der Ergebnisfelder — Gegenstück zur
|
|
Whitelist im Dolibarr-Modul (siehe „Neues Tool")
|
|
toolparams.ts Gedächtnis der zuletzt benutzten Werkzeug-Parameter
|
|
stresstest.ts Auswertung des Dauertests (überlebt App-Wechsel)
|
|
monitor.ts Auswertung der Geräte-Überwachung -> Verfügbarkeit
|
|
wifi/ Kanalgraph-Geometrie, Kanalempfehlung, Bewertung, Demodaten
|
|
overlay.svelte.ts Overlay-Stapel für den Hardware-Zurück-Knopf
|
|
toast.svelte.ts kurze Rückmeldungen
|
|
backButton.svelte.ts Hardware-Back (Single-Instance, KB #480/#549)
|
|
updater.ts APK-Auto-Update-Prüfung (KB #363). Das Token geht als
|
|
Authorization-Header mit, nicht in der Adresse.
|
|
tools/ erweiterbare Tool-Plattform
|
|
index.ts Registry — neues Tool hier eintragen
|
|
netzwerk/ IP-Scan, Port, Ping, IP-Konflikt, SNMP, Traceroute, Stress
|
|
internet/ Durchsatz-Test
|
|
telefonie/ SIP-Erreichbarkeit über OPTIONS (sipcheck.ts) samt
|
|
Bewertung in sip-bewertung.ts. Jede Statuszeile beweist
|
|
eine antwortende Instanz — 401 heißt „erreichbar, will
|
|
Zugangsdaten", nicht „tot". RTP-Sprachqualität bewusst
|
|
NICHT: ohne aufgebautes Gespräch nicht ehrlich messbar.
|
|
src/routes/
|
|
login/ auftraege/ kunden/ einstellungen/
|
|
protokoll/[id]/ Hauptseite mit Werkzeugen, Geräten, Messungen
|
|
protokoll/[id]/stresstest/ monitor/ iptest/ wifi/ wifikanal/
|
|
Werkzeuge mit Live-Anzeige — eigene Seite statt
|
|
Dialog, weil sie über Minuten/Stunden laufen
|
|
android/ natives Android-Projekt inkl. Kotlin-Plugin (einzige Quelle)
|
|
native-plugin/ nur noch Anleitung/Hinweise zum Plugin
|
|
tools/ Prüfwerkzeuge ohne Gerät (siehe unten)
|
|
```
|
|
|
|
## Prüfwerkzeuge (`tools/`, laufen ohne Handy)
|
|
|
|
```bash
|
|
node tools/render-chart.mjs # rendert die echten Diagramm-Komponenten
|
|
# nach .render/*.svg — ansehen, nicht annehmen
|
|
node tools/pruefe-monitor.mjs # rechnet die Verfügbarkeits-Auswertung gegen
|
|
# konstruierte Fälle mit Handwerten durch
|
|
node tools/pruefe-kette.mjs # Internet-Kette: Fehlerglied, Anmeldeseite
|
|
node tools/pruefe-dns.mjs # DNS: NXDOMAIN ist KEIN Ausfall
|
|
node tools/pruefe-geraetediff.mjs # Geräte-Vergleich, alle Fälle OHNE MAC
|
|
node tools/pruefe-geraeteaktionen.mjs # Sortierung nach Gerätetyp
|
|
node tools/pruefe-serveradresse.mjs # http:// bei https-App abweisen
|
|
node tools/pruefe-sip.mjs # SIP: 401/405 sind ERREICHBAR, kein Ausfall
|
|
node tools/test-recommend.mjs # Kanalempfehlung
|
|
```
|
|
|
|
**Warum es diese Prüfer gibt:** Der Browser-Mock liefert bei Netzwerkwerkzeugen
|
|
grundsätzlich den guten Fall — er gibt sogar jedem Gerät eine MAC-Adresse, die
|
|
beim Kunden nie ankommt. Wer im Mock klickt, prüft den Mock. Deshalb liegt die
|
|
Bewertungslogik jeweils in einer Datei **ohne Laufzeit-Import**
|
|
(`*-bewertung.ts`, `geraete-diff.ts`, `serveradresse.ts`), die Node dank
|
|
Type-Stripping direkt laden kann. Jeder dieser sechs Prüfer hat mindestens
|
|
einen echten Fehler gefunden (Muster und Bilanz: KB #1091).
|
|
|
|
`render-chart.mjs` gibt es, weil die erste Fassung der WLAN-Diagramme unbemerkt
|
|
unlesbar in den Einsatz ging: der Emulator hat keine WLAN-Hardware, also war
|
|
nie ein gefülltes Diagramm zu sehen. SVGs mit `rsvg-convert x.svg -o x.png`
|
|
in ein ansehbares Bild wandeln.
|
|
|
|
## Neues Tool hinzufügen
|
|
|
|
1. Datei unter `src/lib/tools/<kategorie>/<id>.ts` anlegen, `Tool` implementieren.
|
|
2. In `src/lib/tools/index.ts` importieren und in `TOOLS` eintragen.
|
|
3. `quickRun: true` setzen, **wenn** das Werkzeug ohne Eingabe sinnvoll startet
|
|
(kurzer Tap startet dann sofort, langes Halten öffnet die Optionen).
|
|
Ausdrücklich je Werkzeug entscheiden — es gibt kein `required` an den
|
|
Parametern, die Pflicht steht allein im `run()`-Rumpf. `portscan.ports` hat
|
|
keinen Vorgabewert und ist trotzdem optional, `iperf.host` hat ebenfalls
|
|
keinen und ist Pflicht.
|
|
4. **Jeden neuen Ergebnis-Schlüssel in BEIDE Feldtabellen eintragen:**
|
|
- `src/lib/messfelder.ts` (App-Anzeige)
|
|
- `netdiagKundenfelder()` in `dolibarr-modul/netdiag/lib/netdiag.lib.php`
|
|
— das ist eine **harte Whitelist**: was dort fehlt, verschwindet im
|
|
Kunden-PDF spurlos, übrig bleibt die nackte Ampel.
|
|
Ebenso den Klarnamen des Werkzeugs in `netdiagToolName()`, sonst steht beim
|
|
Kunden „[netzwerk] meintool".
|
|
|
|
> Punkt 4 ist der Schritt, der hier schon **zweimal** vergessen bzw. geraten
|
|
> wurde. Beim ersten Mal wurde die Whitelist gegen selbst erfundene Testdaten
|
|
> gebaut, beim zweiten Mal stand `'typ'` darin statt des real gelieferten
|
|
> `'type'` — im Abnahmebeleg für eine Netzwerkdose fehlte dadurch die Angabe,
|
|
> ob per LAN oder WLAN gemessen wurde. **Feldnamen immer aus dem erzeugenden
|
|
> Code ablesen**, nie aus dem Kopf. (KB #1084)
|
|
|
|
Sync und Datenbank brauchen keinen Eingriff — das Ergebnis ist generisches JSON.
|
|
Braucht das Tool eine neue native Messroutine: Methode im Kotlin-Plugin,
|
|
Eintrag im Interface `NetDiagScannerPlugin` **und** ein Zweig im Browser-Mock
|
|
(beides in `src/lib/scanner.ts`) — ohne Mock lässt sich nichts ohne Gerät
|
|
prüfen, und ein Mock, der nur den Gutfall zeigt, bestätigt genau die Annahme,
|
|
die im Feld nicht gilt (KB #1086).
|
|
|
|
## Anmeldung
|
|
|
|
Die App hängt am zentralen Auth-Modul **awlauth** (7. AWL-App). Das Token kommt aus
|
|
`auth.php` und geht bei jeder Anfrage als `Authorization: Bearer` mit — auch beim
|
|
APK-Download und beim PDF-Abruf. **Nie in der Adresse:** ein Token in der URL steht in
|
|
jedem Zugriffs- und Proxy-Log und gilt sieben Tage für die gesamte Kunden-API.
|
|
(Einzige, befristete Ausnahme: `update.php?download=1` für App-Fassungen im Feld —
|
|
siehe ChangeLog des Moduls 1.3.0.)
|
|
|
|
### Fingerabdruck statt Passwort
|
|
|
|
Einschaltbar unter Einstellungen → Konto. Was dabei passiert:
|
|
|
|
1. Das Sitzungs-Token wird mit einem AES-256/GCM-Schlüssel aus dem **Android-Keystore**
|
|
verschlüsselt (`setUserAuthenticationRequired`), Chiffrat und IV liegen in
|
|
`shared_prefs/netdiag_biometrie.xml`.
|
|
2. Die **Klartextfassung wird entfernt** (`Preferences.remove('token')`). Genau das ist
|
|
der Sicherheitsgewinn: vorher stand das Token offen in `CapacitorStorage.xml` und war
|
|
auf einem entsperrten Gerät ohne Weiteres lesbar.
|
|
3. Beim nächsten Start zeigt die Login-Maske „Mit Fingerabdruck anmelden". Der Finger
|
|
entschlüsselt das Token; es bleibt danach nur im Arbeitsspeicher.
|
|
|
|
Kein zweiter Faktor — der Finger **ersetzt** das Passwort (Vorgabe von Eddy). Die
|
|
Gültigkeit bestimmt weiterhin `AWLAUTH_TTL`, und „Gerät abmelden" in der
|
|
awlauth-Geräteliste wirkt unverändert: hinterlegt ist dasselbe Token. Wird es
|
|
serverseitig ungültig (401 **mit** Token), räumt die App die Hinterlegung selbst weg.
|
|
Ein 401 **ohne** Token ist dagegen nur „noch nicht angemeldet" und lässt sie stehen —
|
|
diese Unterscheidung ist nicht theoretisch: ohne sie löschte der erste Startaufruf die
|
|
gerade eingerichtete Anmeldung wieder.
|
|
|
|
Wird dem Gerät ein weiterer Fingerabdruck hinzugefügt, macht Android den Schlüssel
|
|
ungültig (`setInvalidatedByBiometricEnrollment`) — die App sagt das im Klartext und
|
|
verlangt einmal das Passwort. Gewollt: sonst könnte jemand mit dem entsperrten Gerät
|
|
einfach seinen eigenen Finger anlernen.
|
|
|
|
WebAuthn wie in den PWAs geht hier nicht — im Capacitor-WebView ist es nicht verlässlich
|
|
verfügbar (KB #855).
|
|
|
|
## Bedienung
|
|
|
|
1. **Anmelden** (Dolibarr-Zugang; auf dem Gerät zusätzlich Server-URL).
|
|
2. **Aufträge** — aktive direkt sichtbar, abgeschlossene per Checkbox, Suche.
|
|
Alternativ über **Kunden** suchen.
|
|
3. Auftrag/Kunde antippen → Diagnose-Protokoll öffnet sich.
|
|
4. **Werkzeuge** ausführen (IP-Scan füllt die Geräteliste, je Gerät weitere Tools).
|
|
Bei den meisten Karten steht „Tippen startet · Halten für Optionen": kurzer
|
|
Tap startet sofort mit den zuletzt benutzten Werten, langes Halten öffnet den
|
|
Parameterdialog. Nach der Messung springt die Ansicht zum frischen Ergebnis.
|
|
5. **Abschließen & synchronisieren** — Protokoll geht ans Dolibarr, PDF landet
|
|
im ECM. Offline bleibt es lokal und synct automatisch bei Verbindung.
|
|
|
|
Selten gebrauchte Aktionen (Protokoll löschen) liegen hinter den drei Punkten
|
|
in der Kopfzeile — bewusst nicht neben dem grünen Abschließen-Knopf.
|
|
|
|
## Ohne Gerät prüfen
|
|
|
|
- **Browser** (`npm run dev`): Mock-Scanner liefert Beispieldaten.
|
|
- **WLAN-Demomodus** (Einstellungen → WLAN-Demodaten): 18 erfundene Netze mit
|
|
allen Grenzfällen — Mesh, Co-Channel, 40 MHz im 2,4-GHz-Band, DFS, 160 MHz,
|
|
Bandrand. Nötig, weil der Emulator keine WLAN-Hardware hat und jeder Scan
|
|
null Netze liefert. Gespeicherte Momentaufnahmen werden als Demo gekennzeichnet.
|
|
- **Diagramme**: `tools/render-chart.mjs` (siehe oben).
|