Commit graph

48 commits

Author SHA1 Message Date
bb6681ac37 Reiter am Kunden nur bei vorhandenen Protokollen (1.3.1) [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 13s
Der Reiter stand auf jeder Kundenkarte; die Leiste dort traegt ueber 20
Eintraege, auf Prod gibt es 20 Protokolle bei 6 von 111 Kunden. Er
erscheint jetzt nur bei Bestand, mit Anzahl. Am AUFTRAG bleibt er fest.

Als Hook (class/actions_netdiag.class.php, completeTabsHead) statt
festem Tab, Muster wie bei Mahnung und ElektroPlanung. Uebernimmt einen
bereits vorhandenen Eintrag desselben Schluessels statt einen zweiten
anzuhaengen — kein doppelter Reiter auch vor der Reaktivierung.

Lokal verifiziert: Kunde mit 13 Protokollen zeigt 'Netzwerk-Diagnose 13'
einmalig, Kunde ohne Protokoll zeigt den Reiter gar nicht.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 21:27:51 +02:00
087f37afa6 1.3.0: PDF-Zweig SIP-Erreichbarkeit, Token in der URL nur noch fuer den APK-Download [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 14s
PDF-Zweig netdiagPdfSip()
- Eigener Zweig, weil die Aussage sonst untergeht: in einer flachen
  Schluessel/Wert-Liste sieht "401 Unauthorized" wie ein Fehler aus, ist aber
  der Beweis fuer eine erreichbare Instanz, die nur Zugangsdaten will. Erst
  Befund im Klartext, dann eine Zeile je Transportweg.
- Der Grenzhinweis steht mit im Dokument: gemessen ist die Erreichbarkeit,
  NICHT die Sprachqualitaet.
- Feldnamen-Falle wieder aufgetreten: 'port' gehoert in beiden Feldtabellen
  laengst dem SNMP-Werkzeug ("Switch-Port"). Im Kunden-PDF haette gestanden
  "Switch-Port: 5060" - deshalb 'zielPort'.

WLAN-Kanalanalyse: Warnungen nicht mehr abschneiden
- dol_trunc(..., 160) kappte genau die Begruendung ("... In dicht besiedelter
  Umgebung meist ein Fehle..."). Eine Warnung ohne ihren Grund ist im
  Kundendokument wertlos; MultiCell bricht ohnehin um.

Token in der Adresszeile
- netdiag_api_read_token() nimmt ?jwt= nur noch an, wenn der Endpunkt es
  ausdruecklich erlaubt. Einzige Stelle: update.php?download=1.
- Grund: ein Token in der URL steht in jedem Zugriffs- und Proxy-Log und gilt
  sieben Tage fuer die GESAMTE Kunden-API.
- Warum die Ausnahme bleibt: die App-Fassungen im Feld bauen die
  Download-Adresse mit dem Token darin. Sofort schliessen hiesse, genau die
  Geraete vom Update auszusperren, die die neue APK brauchen. Zu entfernen,
  sobald Eddy den Rollout bestaetigt (Hinweis steht im Code).
- pdf.php nimmt ab sofort ausschliesslich den Authorization-Header.

Gegen die Testinstanz gemessen: ?jwt= liefert bei orders/customers/protocols/
pdf/update-Version jetzt 401, mit Bearer weiterhin 200.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 21:40:26 +02:00
a0dc0750cb README: Anmeldung nur noch ueber awlauth, PDF-Renderer und netdiagPdfText ergaenzt
All checks were successful
Deploy netdiag / deploy (push) Has been skipped
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 18:05:26 +02:00
0779440347 Trigger: Modul-Deploy [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 13s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:55:53 +02:00
219d489951 Anmeldung nur noch ueber AWL-Auth - eigener JWT-Pfad entfernt
ACHTUNG, Schnitt: Geraete mit einer App-Fassung, die noch ein modul-eigenes
netdiag-JWT benutzt, koennen sich nicht mehr anmelden. Eddy hat den
APK-Rollout am 17.08.2026 als abgeschlossen bestaetigt.

Mehr als Aufraeumen: Der alte Pfad pruefte die Signatur eines selbst
ausgestellten Tokens und holte damit einen Benutzer aus der Datenbank - an der
Sitzungsliste von awlauth vorbei. "Geraet abmelden" hatte darauf keine
Wirkung; ein verlorenes Handy blieb bis zum Ablauf der TTL angemeldet. Jetzt
gibt es genau eine Stelle, an der Sitzungen entstehen und enden.

Entfernt: der Rueckfallweg in auth.php (ohne aktives AWL-Auth jetzt 503 statt
zweitem Weg), netdiag_jwt_encode/decode/secret samt Base64-URL-Helfern, die
Konstanten NETDIAG_API_JWT_SECRET und NETDIAG_API_TOKEN_TTL aus dem
Descriptor, das TTL-Feld aus dem Setup. Bestehende llx_const-Werte bleiben
stehen - ein Loeschlauf beim Modul-Update waere das groessere Risiko.

Die Gueltigkeit kommt jetzt allein aus AWLAUTH_TTL (awlauth-Setup, Standard
7 Tage). Genau darauf hat Eddy hingewiesen: das ist im Auth-Modul geregelt.

Sprachdateien de_DE und en_US nachgezogen, beide wieder deckungsgleich.

Geprueft gegen die Testinstanz: falsches Passwort -> sauberer awlauth-Fehler,
ungueltiges Token -> 401, kein Token -> 401. Kein PHP-Fehler durch die
entfernten Funktionen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:55:53 +02:00
9bea09845f Trigger: Modul-Deploy [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 15s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:13:41 +02:00
4fcd356972 PDF-Zweig und Feldtabelle fuer den Geraete-Vergleich
netdiagPdfGeraeteDiff() gibt die Listen getrennt aus: neu, nicht mehr
erreichbar, mit neuer IP-Adresse, gleiche Adresse mit anderen offenen Ports.
Beim Wiederholungstermin zaehlt, WELCHES Geraet neu ist, nicht nur wie viele.

An jeder Zeile steht, worueber das Geraet wiedererkannt wurde - "erkannt ueber
IP und Portmuster" ist eine schwaechere Aussage als "ueber Netzwerkname", und
der Unterschied gehoert ins Dokument statt in eine Fussnote.

Geprueft im Durchstich: in der App gegen echte Serverdaten gemessen
(ND2026-0016 gegen ND2026-0006), synchronisiert, PDF erzeugt und angesehen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:13:41 +02:00
8fc35948ee Datenkorrektur ausgefuehrt: drei dhcpcheck-Altmessungen auf "nicht messbar"
All checks were successful
Deploy netdiag / deploy (push) Has been skipped
Prod-DB, 17.08.2026, auf Eddys Anweisung. rowid 5, 7 und 10 (Protokolle
ND2026-0002, ND2026-0001, ND2026-0012) stehen jetzt auf measure_status 3 mit
dem Label "DHCP-Server nicht ermittelbar" statt auf 2 mit "0 DHCP-Server (!)".
rowid 42 ist eine echte Messung und blieb unangetastet.

Die Spalte result wurde bei keiner Zeile angefasst - sie ist die Messung
selbst und damit Beleg. Korrigiert wurden nur Bewertung und deren
Zusammenfassung. Ergebnis ueber prod-db-read.sh verifiziert.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 16:45:15 +02:00
4f65eef079 Datenkorrektur: richtigen Ausfuehrungsweg dokumentiert
All checks were successful
Deploy netdiag / deploy (push) Has been skipped
Der zuvor notierte Weg ueber Host 192.168.155.1 ist falsch - das ist die
Docker-interne Adresse und von aussen nicht erreichbar. Richtig ist SSH auf
Unraid plus docker exec in den Container 91-Firma-MariaDB (nicht 90-, wie die
SessionStart-Doku behauptet). Steht so in KB #839.

Ausserdem im Kopf der Datei vermerkt, warum ich die Korrektur nicht selbst
ausfuehre: Schreibzugriffe auf die Prod-DB sind per Konstruktion
zustimmungspflichtig, so steht es woertlich im Kopf von prod-db-read.sh.
Lesend pruefen geht ueber dasselbe Skript ohne Nachfrage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 16:32:26 +02:00
dc716709d5 Datenkorrektur fuer die drei dhcpcheck-Altmessungen vorbereitet
All checks were successful
Deploy netdiag / deploy (push) Has been skipped
Eddy hat die Korrektur der Prod-Datensaetze angewiesen. Das SQL liegt jetzt
unter doc/datenkorrekturen/ - bewusst NICHT unter sql/, weil alles dort von
Dolibarrs _load_tables() beim Aktivieren des Moduls automatisch ausgefuehrt
wird und eine Datenkorrektur in keinen automatischen Lauf gehoert.

Enthaelt Begruendung, den vollstaendigen Vorher-Zustand aller drei Zeilen
(rowid 5, 7, 10) und den Rueckweg. Die WHERE-Bedingung ist wiederholbar und
im Trockenlauf gegen Prod geprueft: sie trifft exakt diese drei Zeilen,
rowid 42 (echte Messung mit echtem Ergebnis) bleibt unangetastet.

Korrigiert werden nur measure_status und label - also die Bewertung und deren
Zusammenfassung. Die Spalte result bleibt unveraendert, sie ist die Messung
selbst und damit Beleg.

Ausgefuehrt ist die Korrektur noch nicht: der Sicherheitsfilter der Sitzung
blockiert Schreibzugriffe auf die Produktionsdatenbank.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 16:24:02 +02:00
2028184f70 Trigger: Modul-Deploy [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 14s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 06:54:55 +02:00
75c1446e76 PDF-Zweig fuer die DNS-Pruefung
netdiagPdfDns(): je befragtem Server eine eigene Zeile statt einer
|-getrennten Kette. Bei zwei Servern ging die generische Darstellung noch,
bei den vier bis fuenf Eintraegen eines Firmennetzes waere die eigentliche
Aussage - welcher Server antwortet und welcher nicht - nicht mehr auffindbar
gewesen. Damit hat jedes Werkzeug mit Listenergebnis einen eigenen Renderer.

Geprueft im vollstaendigen Durchstich: in der App gemessen (Emulator gegen die
lokale Testinstanz), synchronisiert, PDF aus ND2026-0016 erzeugt und angesehen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 06:54:55 +02:00
8f23d417a3 Trigger: Modul-Deploy [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 14s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 06:41:17 +02:00
2c5f7cf11b Feldtabelle fuer das Werkzeug "DNS-Pruefung"
gefragteServer und antworten in die Kunden-Whitelist, dnscheck in die
Werkzeugliste. Ohne diese drei Zeilen stuende im Kundendokument nur die
Ampel - die einzelnen Serverzeilen sind das eigentliche Ergebnis.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 23:24:47 +02:00
0f37cae9e5 Drei Falschaussagen im Kundendokument, alle aus Altdaten
"0 DHCP-Server (!)" in Rot: In der Produktionsdatenbank stehen drei
ausgelieferte Messungen des entfernten Werkzeugs dhcpcheck mit Ergebnis
{"count":0,"server":[],"hinweis":""} und Status 2. Bei Kabelverbindung gibt
Android die DHCP-Angaben aber grundsaetzlich nicht heraus (nur DhcpInfo, nur
WLAN, deprecated) - gemessen wurde also nichts, "0 gefunden" war der
Rueckgabewert fuer "nicht ermittelbar". netdiagAltlastKorrektur() zeigt diese
Messungen jetzt als "nicht messbar" mit Erklaerung, in PDF und
Technikeransicht. Rohdaten bleiben unangetastet, korrigiert wird nur die
Darstellung - und die wird bei jedem Abruf neu erzeugt. Die Ergebniszahlen
werden dabei unterdrueckt: "Gefundene Geraete: 0" widerspraeche der Aussage
direkt darueber.

"Lease-Dauer: 864000 s s": aeltere App-Fassungen haben die Einheit in den Wert
geschrieben, die Feldtabelle haengt sie erneut an (Prod-Messung #42).

Adress- und Lease-Felder mit dem Wert 0 erscheinen jetzt als "nicht
ermittelbar" statt als "0" - das war nie eine Messung, sondern das
"nichts ermittelt" der alten Android-API.

Neu in der Whitelist: dhcpQuelle (system = LinkProperties ab Android 11 fuer
jeden Anschlusstyp, wlan = alter Weg nur fuer WLAN). Damit ist klaerbar, ob
eine fehlende DHCP-Angabe an der Leitung lag oder an der Android-Version.

Geprueft gegen die lokale Testinstanz mit einer Kopie der echten
Prod-Messungen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 23:11:07 +02:00
b2ab430ae7 Internet-Kette im PDF + Pfeile wurden als "?" gedruckt
netdiagPdfKette() fuer das neue App-Werkzeug: Fehlerglied als eigene
hervorgehobene Zeile, darunter die vier Stufen einzeln, dann Systemurteil
und Hinweis. Ohne eigenen Zweig stuenden die Stufen als |-Kette in einer
Zelle und das Fehlerglied - die eigentliche Aussage - mittendrin.

Dabei ein Fehler gefunden, der jedes Protokoll betrifft: pdf_getPDFFont()
liefert Helvetica, einen Core-Font mit WinAnsi-Kodierung. Alles ausserhalb
dieses Zeichenvorrats setzt TCPDF wortlos als "?" (KB #1025). Aufgefallen
an "www.google.de -> 142.250.185.67", das im Kundendokument als
"www.google.de ? 142.250.185.67" ankam. netdiagPdfText() ersetzt die
betroffenen Zeichen jetzt in ALLEN PDF-Zweigen, also auch fuer Altdaten.
Bewusst keine Unicode-Schrift: dejavusans wuerde Schriftbild und Dateigroesse
aller Protokolle aendern. Gedankenstrich, Mittelpunkt und Anfuehrungszeichen
sind in WinAnsi enthalten und bleiben unangetastet (nachgeprueft).

Feld-Whitelist um fehlerglied, stufen, systemUrteil, validated und
captivePortal erweitert. Die letzten beiden kommen aus dem LinkInfo-Objekt
und erscheinen damit auch beim IP-Test an der Dose.

Geprueft an vier Faellen gegen die lokale Testinstanz, zwei davon die echten
Emulator-Messungen dieser Nacht (Gutfall und Flugmodus).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 22:59:49 +02:00
36dd18f53a Trigger: Modul-Deploy [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 13s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 22:24:57 +02:00
90cbee6e81 Geraete-Monitor: eigener PDF-Zweig statt einer Sammelzelle
Die Dauerueberwachung lief im generischen else-Zweig und wurde von
netdiagPdfFlattenResult() zu EINER |-getrennten Zeile zusammengeschoben.
Bei zehn ueberwachten Geraeten standen damit die zehn Geraete-Zeilen und
saemtliche Einzelausfaelle in einer einzigen Tabellenzelle - vollstaendig,
aber beim Kunden nicht lesbar. Dauertest und WLAN-Kanal hatten laengst je
einen Tabellen-Renderer, der Monitor nicht.

netdiagPdfMonitor() gibt jetzt aus: Kennzahlenzeile, Tabelle "Ueberwachte
Geraete", Tabelle "Einzelne Ausfaelle" mit Uhrzeiten, Hinweis zum
Messverfahren am Schluss. Tabellenkoepfe wiederholen sich nach einem
Seitenumbruch.

Die Falle dabei, gegen die im Code ein Kommentar steht: beim Dauertest
fuehrt ein gesetztes 'hinweis' zum sofortigen Abbruch der Ausgabe. Der
Monitor legt aber IMMER einen Hinweis an (das Messverfahren gehoert zur
Aussage dazu) - derselbe Aufbau haette hier bei jedem Lauf alle Zahlen
verschluckt. Nur 'fehler' bricht ab.

Obergrenze 200 Ausfallzeilen gegen ein im Sekundentakt flappendes Geraet.
Gekuerzt wird sichtbar ("... und N weitere Ausfaelle") - stilles Kuerzen
waere bei einem Abnahmebeleg eine Falschaussage.

Ausserdem: 'ausfallzeitSek' heisst jetzt "Ausfallzeit (schlechtestes
Geraet)". Der Wert ist der des schlechtesten Geraets, nicht die Summe -
bei zehn Geraeten las sich "Ausfallzeit: 8 min" wie eine Gesamtaussage.

Geprueft gegen die lokale Testinstanz mit vier konstruierten Faellen
(10 Geraete mit Aussetzern, ein durchgehend totes Geraet, 250 Ausfaelle
ueber sechs Seiten, abgerissener Lauf) und gegen die echte Monitor-
Messung aus ND2026-0016. PDF gerendert und angesehen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 22:22:02 +02:00
9f07896b05 Doku + Version 1.1.1: die Whitelist-Falle steht jetzt im README [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 13s
Version auf 1.1.1 gezogen — auf Prod lief sonst eine 1.1.0, die nicht der
1.1.0 im Repo entspricht (die vier Bugfixes gingen ohne Versionssprung raus).

ChangeLog 1.1.1 mit allen vier Fehlern, der Rechtelücke im GET-Zweig und den
neuen Monitor-Feldern.

README:
- Neuer Abschnitt "Neue Messart aus der App aufnehmen" mit den DREI Stellen in
  netdiag.lib.php und der Spalte "wenn vergessen". Das ist die Falle, in die
  hier schon zweimal getappt wurde - einmal mit erfundenen Testdaten, einmal
  mit 'typ' statt 'type'. Feldnamen immer aus dem erzeugenden App-Code ablesen.
- Anmeldung laeuft ueber awlauth (stand noch "JWT-Auth")
- Rechteprüfung je Endpunkt dokumentiert, inkl. der Begruendung, warum der
  GET-Zweig auch 'write' akzeptiert
- Hinweis, dass '?jwt=' nur fuer den PDF-Download existiert und in Logs landet
2026-08-16 19:56:10 +02:00
28a7fa9f79 Trigger: Modul-Deploy [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 13s
2026-08-16 19:45:38 +02:00
a39688a74e Monitor-Felder in die Kundenfeld-Whitelist
Der Geraete-Monitor liefert ab sofort eine richtige Messung (App-Seite).
Ohne diese sechs Schluessel stuende im Kunden-PDF nur die Ampel — genau der
Fehler, der bei der Whitelist schon einmal passiert ist.

geraeteAnzahl, verfuegbarkeitProzent, ausfallzeitSek, aussetzer, geraete,
ausfaelle. Die Werte sind der Beleg fuer "das Netz war ueber X Stunden stabil"
bzw. fuer die haeufigste Reklamation ("die Kamera faellt staendig aus").
2026-08-16 19:28:57 +02:00
3d12ee1e61 Vier Bestandsfehler im Kundendokument und im Sync behoben
Alle vier durch eine systematische Gegenpruefung des echten Codes gefunden,
nicht durch Symptome — sie waren bereits ausgeliefert und still.

1. Whitelist-Schluessel 'typ' existiert nicht. Die App liefert das
   LinkInfo-Objekt durch, dort heisst das Feld 'type' (app/src/lib/types.ts).
   Folge: im Kundendokument fehlte ausgerechnet beim IP-Test an der Dose die
   Angabe, ob per LAN oder WLAN gemessen wurde - bei einem Abnahmebeleg die
   halbe Aussage. Zusaetzlich 'rssi' aufgenommen: bei einer WLAN-Dose IST der
   Empfangspegel der Messwert, er wurde bisher verworfen.
   Die Rohwerte sind englisch; netdiagKundentext() bildet sie jetzt ab
   ('ethernet' -> 'LAN (Kabel)'), sonst stuende "Anschlussart: ethernet" im
   Abnahmeprotokoll eines deutschen Handwerksbetriebs.

2. Leeres Array druckte "Offene Ports: " ohne Wert. Die Leerpruefung ist ein
   elseif hinter dem Array-Zweig und wurde nie erreicht. Betraf ausgerechnet
   die GUTEN Ergebnisse: Portscan ohne offenen Port, IP-Konflikt ohne
   Konflikt, IP-Scan ohne Veraenderung. Jetzt "keine" - Weglassen waere
   schlechter, weil der Kunde sonst nicht unterscheiden kann, ob nichts
   gefunden oder nichts geprueft wurde.

3. 'error' fehlte in der Whitelist. Der IP-Konflikt gibt im Abbruchfall
   { error: ... } mit roter Ampel zurueck - im Kundendokument stand eine rote
   Ampel ohne einen Buchstaben Erklaerung. Die App vereinheitlicht kuenftig
   auf 'fehler', aber Altdaten lassen sich nicht aendern.

4. tool/category/label wurden beim Sync nicht auf die Spaltenlaenge gekuerzt
   (varchar 64/32/255). Bei striktem SQL-Modus kippt EIN zu langes Label den
   gesamten Sync per rollback() - der Techniker steht beim Kunden mit einem
   Protokoll da, das sich nicht abschliessen laesst, weil eine Beschriftung zu
   lang war. Die App setzt Labels durch Verketten zusammen, 255 Zeichen sind
   erreichbar. found_via wurde 20 Zeilen darueber laengst gekappt.

Ausserdem:
- GET-Zweig der API prueft jetzt eine Berechtigung. Bisher konnte jeder
  angemeldete Benutzer mit einer geratenen ID ein fremdes Protokoll samt
  Geraeteliste, IP-Adressen und offenen Ports abrufen. 'write' wird bewusst
  mit akzeptiert: die Rechte sind in Dolibarr einzeln vergebbar, und ein
  Techniker mit Schreib- ohne ausdruecklichem Leserecht duerfte nicht
  ausgesperrt werden - das waere erst beim Kunden aufgefallen.
- netdiagDauerLesbar: "1 Tage 0 Std" -> "1 Tag". floor() liefert einen Float,
  1.0 === 1 ist in PHP false.

Geprueft: php -l, und netdiagFeldFuerKunde() mit 13 echten Faellen gegen die
lokale Instanz durchgerechnet (LAN/WLAN/Mobilfunk, leere und gefuellte Arrays,
error/fehler, tote Schluessel, Interna).
2026-08-16 19:02:11 +02:00
5bb5756604 Trigger: Modul-Deploy [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 14s
2026-08-16 11:22:18 +02:00
7186be64da Kundenfeld-Whitelist trifft jetzt die real gelieferten Schlüssel
Regression aus dem vorherigen Commit, gefunden bei der Bestandsprüfung von
Phase 6: netdiagKundenfelder() war gegen selbst erzeugte Testdaten gebaut,
nicht gegen die Namen, welche die App-Werkzeuge tatsächlich liefern.

Konkret liefert die App "ziel"/"zielErreicht" (Traceroute), "gegenstelle"/
"downloadMbps"/"uploadMbps" (Durchsatz), "scanned"/"open" (Portscan),
"geprueft"/"runden"/"konflikte" (IP-Konflikt), "port"/"linkSpeed"/
"eingangsFehler"/"ausgangsFehler" (SNMP) sowie das gespreadete LinkInfo beim
IP-Test. Keiner dieser Schlüssel stand in der Tabelle — und weil
netdiagFeldFuerKunde() unbekannte Schlüssel verwirft, wäre im Kundendokument
von diesen fünf Werkzeugen nur noch die Ampel übrig geblieben, ohne einen
einzigen Messwert. Das ist schlechter als der Zustand davor (rohe Schlüssel).

Alle real emittierten Schlüssel ergänzt und gegen die echten Ergebnis-
Strukturen geprüft, Beispiel Portscan:
  "Gerät: 192.168.1.50 | Geprüfte Ports: 10 | Offene Ports: 80/http, 443/https"

Lehre für künftige Werkzeuge: die Tabelle ist eine Whitelist, ein neues
Werkzeug braucht dort einen Eintrag — sonst verschwinden seine Messwerte
stillschweigend aus dem Kundendokument.
2026-08-16 11:22:18 +02:00
d079431cdf Phase 5 abgeschlossen: Klarnamen, Parameter, Listen-Ampel, N+1, Standort
- Werkzeug-Klarnamen statt interner IDs: "IP-Scanner — 46 Geräte im Netz …"
  statt "[netzwerk] ipscan — …". Bewusst eine kurze Zuordnung in
  netdiagToolName() statt eines zweiten Satzes Sprachschlüssel — die
  kanonischen Namen stehen in der App, doppelte Pflege wäre eine Fehlerquelle.
  Enthält auch dhcpcheck/wifiscan: in der App gibt es sie nicht mehr, in der
  PRODUKTIONSDATENBANK stehen dazu aber noch Messungen (4 bzw. 2). Die
  Gegenprüfung hielt den Punkt für gegenstandslos, hatte dabei aber nur die
  Testdatenbank angesehen.

- Messparameter anzeigen (Prod-Messung #126): unter jeder Messung steht jetzt
  "Ziel: 192.168.1.1 · Dauer (s): 300" in Karte und PDF. Vorher stand das
  Ergebnis ohne Bezugspunkt da — man sah nicht, wogegen gemessen wurde.

- Listenseite: Ampel je Protokoll (schlechteste Einzelmessung) plus Anzahl,
  dazu ein Filter "nur mit Befund". Status 3 "nicht messbar" geht bewusst
  NICHT ins Maximum ein — er ist keine Aussage über das Kundennetz — sondern
  wird separat als "n.m." ausgewiesen. Im Browser geprüft: der Filter liefert
  ausschließlich Protokolle mit Warnung oder Fehler.

- N+1-Queries behoben: fetchAllByProtocol() las nur die rowids und setzte je
  Zeile ein eigenes fetch() ab. Jetzt eine Abfrage mit setVarsFromFetchObj(),
  zusätzlich mit Entity-Filter (fehlte bisher ganz). Nachgemessen über
  SHOW SESSION STATUS: 46 Geräte + 7 Messungen brauchen statt 55 Abfragen
  noch eine.

- Standort aus der Kundenadresse vorbelegen, wenn der Techniker nichts
  eingetragen hat; eine vorhandene Angabe wird nie überschrieben. Über die
  echte API geprüft.

Offen bleibt aus Phase 5 nur der Vergleich zweier Protokolle (Geräte-Diff
nach MAC) — eigenes Feature mit eigener Ansicht.
2026-08-16 11:09:28 +02:00
221145736f Trigger: Modul-Deploy [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 13s
2026-08-15 18:54:11 +02:00
0b3db47ec1 Phase 5: Kundendokument lesbar, Gerätemerkmale kommen endlich an
Vorab: drei Punkte der Roadmap-Liste waren Fehlannahmen. Eine Analyse mit
anschließender Gegenprüfung (jeder Befund musste einen Widerlegungsversuch
überstehen) hat sie ausgeräumt, bevor Code geändert wurde:
 - "ab Seite 2 alles nach rechts verschoben" existiert nicht. Nachgemessen am
   Prod-PDF ND2026-0015 mit pdftotext -bbox: Seite 1 und Seite 2 beginnen beide
   bei 16,0 mm. Die echten Umbruchfehler waren andere.
 - measure_status validieren war seit Phase 1 erledigt.
 - Werkzeug-IDs / Teilnetz-Gruppierung / TCPDF-Fußzeile: verworfen, die
   vorgeschlagenen Änderungen hätten das PDF verschlechtert.

PDF (alle Punkte am mehrseitigen Dokument nachgeprüft):
- Tabellenkopf der Geräteliste wird auf Folgeseiten wiederholt. Vorher standen
  ab Seite 2 unbeschriftete Spalten — bei leeren MAC/Hostname-Feldern vier
  namenlose Spalten.
- Messungs-Titelzeile und Ergebnis werden zusammengehalten. Vorher blieb die
  Überschrift samt Ampel am Seitenende allein zurück, darunter ein leerer,
  unten offener Rahmen; in einem Testlauf über 61 Umbruchlagen 5-mal (~8 %).
- Spalte "Gerätetyp" hatte 15 mm, ließ aber 10 Zeichen zu — "Chromecast/TV"
  lief bis 199,0 mm bei 195 mm Tabellenkante über den Rahmen in den Druckrand.
- Deutsche Bezeichnungen mit Einheiten statt roher JSON-Schlüssel: aus
  "VerlustProzent: 0 | MinMs: 4.4 | UptimeSek: 8123456" wird "Paketverlust: 0 %
  | Kürzeste Antwortzeit: 4.4 ms | Betriebszeit: 94 Tage 1 Std". Als Whitelist
  (netdiagKundenfelder(), gemeinsam für Karte und PDF) — interne Felder wie
  arpAvailable, mdnsOk, probed, answered fallen damit automatisch heraus.
- "ARP-Tabelle nicht lesbar (/proc/net/arp) — braucht Root" wird beim Drucken
  zu einem kundentauglichen Satz. Altdaten stehen so in der DB, deshalb
  Ersetzung beim Drucken statt nur in der App.

Gerätemerkmale (der eigentliche Roadmap-Punkt): Der Techniker sah in der App
"Drucker HP, Port 9100", im Kundenprotokoll stand nur die IP. Die Felder
fehlten dabei nicht in der Übertragung, sondern durchgängig — ein Fix allein
in der API wäre folgenlos geblieben, weil Dolibarrs setSaveQuery() nur
deklarierte $fields schreibt. Ergänzt über die ganze Kette:
  sql/llx_netdiag_device.sql + neue Migration llx_netdiag_device_v2.sql
  (ADD COLUMN IF NOT EXISTS, wiederholbar, läuft bei jedem Modul-Update),
  NetDiagDevice::$fields + Properties, api/protocols.php POST und GET,
  Kartenansicht und PDF.
Neu: netbios_name, mdns_name, mdns_services, custom_name, open_ports,
found_via, last_seen. Im PDF steht jetzt statt "192.168.178.20" die Zeile
"Brother HL-L2350DW · Brother · Drucker · 80,443,9100" — der Name kommt aus
mDNS, obwohl der Hostname leer ist.

Sprachschlüssel Vendor -> NetDiagVendor: Die Gegenprüfung hielt den Punkt für
falsch (Translate::load() ist first-wins, im CLI-Test kam "Hersteller"), im
Browser stand in der Kartenansicht aber "Lieferant" — im HTTP-Kontext lädt
Dolibarr vorher andere Sprachdateien als im CLI. Statt der Ursache nachzugehen
jetzt ein eigener, kollisionsfreier Schlüssel; im Browser gegengeprüft.

Nebenbei: doppeltes "OK OK" beim Status 0 im PDF.

Gegen das Test-Dolibarr geprüft: Sync über die echte API (Login, POST, GET),
Felder in der DB kontrolliert, Kartenansicht im Browser, mehrseitiges PDF
gerendert und angesehen, Migration zweimal ausgeführt (idempotent).
2026-08-15 18:54:01 +02:00
8b433726f3 Trigger: Modul-Deploy [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 13s
2026-08-15 17:46:03 +02:00
5e683cf0c7 WLAN-Momentaufnahme aus dem Demomodus kennzeichnen
Die App kann WLAN-Netze zum Ansehen/Vorführen simulieren (Emulator und Geräte
ohne Empfang zeigen sonst nichts). Solche Momentaufnahmen tragen demodaten:true
im Ergebnis — Kartenansicht und PDF weisen das jetzt als eigene Zeile aus.

Im PDF besonders wichtig: das Dokument geht zum Kunden, dort darf eine
Vorführung nie wie eine echte Messung am Standort aussehen.
2026-08-15 17:45:52 +02:00
0426fe2574 Trigger: Modul-Deploy [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 13s
2026-08-15 17:38:04 +02:00
9b69438d17 WLAN-Kanal-Momentaufnahme darstellen (HTML + PDF)
Die App legt eine WLAN-Momentaufnahme jetzt als Messung ab (tool='wifikanal'),
damit sie beim Kunden aufgenommen auch im Protokoll auf dem Server ankommt —
bisher lag sie nur lokal auf dem Handy.

- netdiagFormatWifiKanal(): Zusammenfassung (Anzahl Netze, eigenes Netz mit
  Kanal/Pegel/Bewertung, störungsärmster 2,4-GHz-Kanal), Hinweisliste und
  vollständige Netztabelle. Eigener Zweig wie beim Dauertest, weil die
  generische Darstellung das Netz-Array per dol_trunc(...,200) mitten im Satz
  abschneiden würde.
- netdiagPdfWifiKanal(): dieselbe Aufbereitung als PDF-Tabelle.
- Nebenbei: PDF zeigte bei Status 0 doppelt "OK OK" (Zeichen + Label).

Gegen das Test-Dolibarr geprüft: Kartenansicht rendert Zusammenfassung, beide
Warnhinweise und alle 12 Netze; PDF-Erzeugung ebenfalls verifiziert.
2026-08-15 17:25:13 +02:00
75986ee1c8 Trigger: Deploy [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 14s
Bringt Commit 210e4a1 (Dauertest strukturiert im PDF + Web-Karte) auf Prod.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 06:54:33 +02:00
210e4a1b41 Dauertest strukturiert darstellen (Web-Karte + PDF) statt Fliesstext
Der Dauertest liefert seit App-Phase 2 deutlich mehr Ergebnisfelder
(Zeitreihe in Minuten-Buckets, Ausfallsegmente, p95, laengster Ausfall).
netdiagFormatResult() (Web-Karte) und netdiagPdfFlattenResult() (PDF) wuerden
das zu einem einzigen Fliesstext zusammenkleben — die Web-Karte kuerzt jeden
Wert zusaetzlich per dol_trunc(...,200), eine Ausfallliste mit mehreren
Eintraegen waere also mitten im Satz abgeschnitten.

- netdiagFormatResult($json, $tool='') bekommt einen optionalen zweiten
  Parameter; fuer $tool==='stresstest' greift netdiagFormatStressTest() statt
  der generischen Kuerzung: Kennzahlenzeile (Ziel, Dauer, Takt, Proben,
  Verlust, Oe/Min/Max/p95, laengster Ausfall) + eine echte <ul>-Ausfallliste,
  ungekuerzt. netdiagprotocol_card.php uebergibt jetzt $m->tool.
- netdiagPdfStressTest() im PDF-Generator macht dasselbe mit TCPDF-Zellen:
  Kennzahlenzeile + Ausfalltabelle als eigene Zeilen. Die Zeitreihe
  (`verlauf`) wird bewusst NICHT gedruckt — als Diagramm in der App nuetzlich,
  als Fliesstext auf Papier nicht.
- Beide Formatierer nutzen dieselben vorformatierten Ausfall-Zeilen
  ("HH:MM:SS – HH:MM:SS (Xs)"), die auch die App in ihrer Messungen-Liste
  zeigt — eine Quelle der Wahrheit statt mehrfacher Formatierung.

Lokal verifiziert (Test-Dolibarr, Modul manuell deployt): Web-Karte zeigt
Kennzahlenzeile + Ausfaelle-Liste mit echten Uhrzeiten statt Rohschluessel,
PDF ebenso (2 Seiten, TCPDF-Ausfalltabelle je Lauf lesbar).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 00:40:08 +02:00
f602ab771d Trigger: Deploy [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 13s
Bringt Commit 7df1461 (Bewertung "nicht messbar" + Status-Validierung) auf Prod.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 23:45:53 +02:00
7df146181f Bewertung "nicht messbar" (Status 3) + Status-Validierung
Die App kennt seit Phase 1 eine vierte Bewertung: der Test konnte gar nicht
durchgefuehrt werden (Switch antwortet nicht auf SNMP, kein Netz, Gegenstelle
fehlt). Ohne diesen Zustand musste sich ein fehlgeschlagener Test als ok, warn
oder fail ausgeben — in der Praxis meist als Gruen.

- protocols.php nimmt nur noch Bewertungen 0-3 an. Ein unbekannter Wert landete
  bisher ungeprueft als Array-Index in Karte und PDF und lief dort ins Leere;
  jetzt wird daraus eine Warnung, nie ein OK.
- Protokollkarte und PDF stellen Status 3 grau dar ("Nicht messbar") — weder
  gruen (waere gelogen) noch rot (waere eine Aussage ueber das Kundennetz, die
  die Messung nicht hergibt).
- Das PDF zeigt die Ampel zusaetzlich als Zeichen (OK / ! / X / ?), damit sie im
  Schwarz-Weiss-Ausdruck beim Kunden lesbar bleibt.
- Sprachschluessel NetDiagMeasureUnmeasurable in de_DE und en_US.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 19:16:34 +02:00
f2a5958adb Anmeldung ueber das zentrale Auth-Modul awlauth — v1.1.0 [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 14s
NetDiag war die letzte AWL-App mit eigenem Login: eigenes JWT, eigener Schluessel,
eigene Gueltigkeitsdauer, keine Moeglichkeit ein verlorenes Handy gezielt abzumelden.

- api/auth.php prueft das Passwort ueber awlauth_login(). Damit greifen dort auch die
  Brute-Force-Bremse (5 Fehlversuche je IP in 15 min, 10 je Benutzername in 30 min)
  und die einheitliche, nicht-verraeterische Fehlermeldung. Das Token kommt aus
  awlauth_issue_bearer() und legt eine Sitzungszeile an: das Geraet erscheint als
  "NetDiag-App · Android" in der awlauth-Geraeteliste und ist dort einzeln abmeldbar.
- netdiag_api_authenticate() prueft zuerst das awlauth-Token; ein widerrufenes Token
  fuehrt sofort zu 401, auch wenn die Signatur noch stimmt.
- Uebergangsweise gilt ein bereits ausgestelltes altes netdiag-JWT weiter, damit die
  Umstellung niemanden mitten im Einsatz aussperrt. Faellt weg, sobald alle Geraete
  einmal neu angemeldet sind.
- Ohne aktives awlauth laeuft das Modul unveraendert mit dem eigenen JWT weiter.

Die Antwortform von auth.php bleibt bewusst {token, expiresIn, user}: bereits
installierte APKs laufen nach einer einmaligen Neuanmeldung ohne Update weiter.
Haette man sie geaendert, waeren alle Geraete ausgesperrt — und die neue APK gibt es
nur ueber update.php, das Anmeldung verlangt.

CORS: Wildcard-Origin raus. Die API liefert Kundendaten aus; ein * erlaubt jeder
Webseite die Antwort auszulesen, sobald sie an ein Token kommt. Erlaubt sind jetzt nur
die App-Origins und der Vite-Dev-Server, dazu Vary: Origin. Anfragen ohne Origin
(nativer Client, APK-Downloader im Plugin, curl) sind unveraendert.

Lokal gegen das Test-Dolibarr geprueft: Login liefert awlauth-Token, Sitzungszeile
entsteht, orders.php mit Bearer = 200, falsches Passwort = 401, kein/manipuliertes
Token = 401, 4. Fehlversuch = 429 mit Wartezeit, Alt-Token = 200 (Fallback),
fremder Origin bekommt keinen CORS-Header. Anschliessend im Emulator durchgespielt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 18:23:29 +02:00
68bef9ae5a modNetDiag: Top-Menü-Eintrag im Header deaktiviert [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 14s
Zugriff auf NetDiag erfolgt ausschließlich über die Tabs an Kunde/Auftrag.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 07:52:25 +02:00
4aa26962c9 orders.php: Auftragsliste nach letzter Bearbeitung sortieren [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 13s
- Spalte c.tms in SELECT aufgenommen (Dolibarr-Änderungszeitstempel)
- ORDER BY c.tms DESC statt date_commande DESC — zuletzt bearbeitete
  Aufträge stehen oben, passend zur App-Anzeige "bearb. <Datum>"
- tms-Feld in der API-Antwort ergänzt
- README: Hinweis auf Deploy-Pipeline ergänzt

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 22:05:09 +02:00
5b40c21d90 API-Endpoint update.php — Updater-Backend-Proxy [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 13s
Die App kann die private Forgejo-Registry nicht erreichen (403/CORS) —
darum sagte der In-App-Updater faelschlich sofort 'aktuell'.

update.php prueft die Registry serverseitig (Token aus NETDIAG_FORGEJO_TOKEN)
und liefert der App:
  GET update.php             -> { version }
  GET update.php?download=1  -> die APK (zum Installieren)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 20:36:08 +02:00
db2390d997 Sync-500 behoben: App-Zeitstempel (ms) auf Sekunden umrechnen [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 16s
DER eigentliche Fehler: Die App schickt dateDiag/dateMeasure als
JavaScript-Millisekunden (Date.now(), 13-stellig). Dolibarrs idate()
erwartet Unix-Sekunden -> MySQL: "Incorrect datetime value: Bad value
1779211311036 for date" -> createCommon scheitert -> HTTP 500.

Fix: netdiag_api_timestamp() rechnet ms-Zeitstempel (> 1e11) auf Sekunden
um. protocols.php nutzt sie fuer date_diag und date_measure.

Serverseitig bewusst — so synchronisieren auch bereits installierte
App-Versionen ohne APK-Update korrekt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 19:34:48 +02:00
e914454e0f protocols.php: vollen Fehlertext ausgeben (errorsToString) [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 14s
Der Endpoint gab bei Speicherfehlern nur $obj->error (Singular) aus — das
ist leer, weil CommonObject::createCommon den Grund nach $obj->errors[]
(Array) schreibt. Ergebnis: "Protokoll speichern fehlgeschlagen: " ohne
Grund. Jetzt errorsToString() — liefert error + errors[] zusammen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 19:32:14 +02:00
8be5297196 Sync-500: tms vor create/update explizit setzen [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 14s
Nachtrag zum tms-Fix: explicit_defaults_for_timestamp=1 auf der Prod-DB —
ein INSERT mit tms=NULL in die NOT-NULL-Spalte schlaegt fehl. createCommon
fuegt tms aber als NULL ein, wenn die Property leer ist.

Loesung: protocols.php setzt tms = dol_now() vor jedem create/update von
Protokoll, Geraet und Messung. Damit landet ein gueltiger Zeitstempel im
INSERT, kein NULL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 18:37:48 +02:00
ddb7161b00 Sync-500 behoben: tms-Feld auf notnull=0 [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 14s
protocols.php (POST) gab bei JEDEM Sync HTTP 500 zurueck — 0 Protokolle
wurden je gespeichert.

Ursache: protocol/device/measurement-Klassen deklarierten das tms-Feld im
$fields-Array als 'notnull' => 1. Dolibarrs createCommon() bricht ab (-1),
wenn ein notnull-Feld den Wert NULL hat und kein 'default' gesetzt ist.
tms wird von der App nie gesetzt (die DB fuellt es per CURRENT_TIMESTAMP),
also schlug jedes Insert fehl, bevor es ueberhaupt an die DB ging.

Fix: tms auf 'notnull' => 0 — so wie es der Dolibarr-Modul-Builder auch
generiert. Die DB-Spalte bleibt NOT NULL DEFAULT CURRENT_TIMESTAMP.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 18:28:58 +02:00
914101ba30 API-Endpoint applog.php — Debug-Log der mobilen App empfangen [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 14s
Die App erfasst Fehler/Meldungen und schiebt sie per POST hierher; sie
landen in llx_netdiag_applog und sind serverseitig auswertbar — ohne Kabel.
- api/applog.php: JWT-Auth (wie alle Endpoints), Batch-Insert, legt die
  Tabelle bei Bedarf an (Modul kann schon vorher installiert gewesen sein)
- sql/llx_netdiag_applog.sql + .key.sql: Tabelle fuer saubere Neuinstallation

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 17:39:31 +02:00
Eduard Wisch
baa7df88e4 Auftragsliste: Adresse und öffentliche Notiz mitliefern [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 14s
Die App zeigt Aufträge jetzt nach Kunde + Ort statt nach Nummer —
dafür liefert orders.php in der Liste zusätzlich s.address und
c.note_public.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 12:57:06 +02:00
Eduard Wisch
2989ec10ed Fix: master.inc.php im globalen Scope laden — behebt 500 beim Login [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 14s
netdiag_api_bootstrap() includete master.inc.php innerhalb der Funktion.
Dadurch landeten $conf/$db/$langs/$user im Funktions-Scope und waren
nach return weg — der erste DB-Zugriff (checkLoginPassEntity -> global
$db) lief gegen null: Call to a member function query() on null.
master.inc.php wird jetzt im File-Scope der Lib geladen, die Funktion
macht nur noch CORS + OPTIONS-Preflight.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 12:49:15 +02:00
Eduard Wisch
44abdbbc95 Modul-ID auf 500300 — 500100 kollidierte mit globalnotify [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 18s
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 12:15:33 +02:00
Eduard Wisch
c576726a26 Initiales Commit — Dolibarr-Modul NetDiag [deploy]
Some checks are pending
Deploy netdiag / deploy (push) Waiting to run
Netzwerk-Diagnose-Modul mit JSON-API für die NetDiag-App:
- 3 Tabellen (protocol/device/measurement), generisches JSON-result
- JSON-API: auth, customers, orders, protocols (idempotenter Sync), pdf
- JWT-Auth (HS256), CORS für die Capacitor-App
- Tabs an Thirdparty + Auftrag, Protokoll-Card, PDF-Generator
- QR-Code zum App-Download in der Modul-Konfiguration
- de_DE + en_US, Rechtesystem netdiag->protocol read/write/delete

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 12:12:11 +02:00