Compare commits
3 commits
5bb5756604
...
28a7fa9f79
| Author | SHA1 | Date | |
|---|---|---|---|
| 28a7fa9f79 | |||
| a39688a74e | |||
| 3d12ee1e61 |
2 changed files with 91 additions and 6 deletions
|
|
@ -42,6 +42,21 @@ require_once __DIR__.'/../class/netdiagmeasurement.class.php';
|
||||||
// GET: einzelnes Protokoll mit Geräten und Messungen
|
// GET: einzelnes Protokoll mit Geräten und Messungen
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
// Leserecht prüfen. Der POST-Zweig weiter unten verlangt seit jeher
|
||||||
|
// 'protocol write', der GET-Zweig prüfte bis 16.08.2026 gar nichts: jeder
|
||||||
|
// angemeldete Benutzer konnte mit einer geratenen ID ein fremdes Protokoll
|
||||||
|
// samt Geräteliste, IP-Adressen und offenen Ports abrufen. Das ist beim
|
||||||
|
// Kunden erhobene Netzstruktur — sie gehört hinter ein Recht.
|
||||||
|
//
|
||||||
|
// 'write' wird bewusst mit akzeptiert: In Dolibarr sind die Rechte einzeln
|
||||||
|
// vergebbar, ein Techniker-Benutzer mit Schreib- aber ohne ausdrückliches
|
||||||
|
// Leserecht ist möglich. Der würde sonst genau die Protokolle nicht mehr
|
||||||
|
// abrufen können, die er selbst hochgeladen hat — die App wäre nach dem
|
||||||
|
// Update kaputt, und zwar erst beim Kunden. Wer schreiben darf, darf lesen.
|
||||||
|
if (!$user->hasRight('netdiag', 'protocol', 'read')
|
||||||
|
&& !$user->hasRight('netdiag', 'protocol', 'write')) {
|
||||||
|
netdiag_api_error('Keine Leseberechtigung', 403);
|
||||||
|
}
|
||||||
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
||||||
if ($id <= 0) {
|
if ($id <= 0) {
|
||||||
netdiag_api_error('Parameter id fehlt', 400);
|
netdiag_api_error('Parameter id fehlt', 400);
|
||||||
|
|
@ -217,9 +232,17 @@ foreach ($measIn as $m) {
|
||||||
$meas->fk_protocol = $protocolId;
|
$meas->fk_protocol = $protocolId;
|
||||||
$dcid = isset($m['deviceClientId']) ? (string) $m['deviceClientId'] : '';
|
$dcid = isset($m['deviceClientId']) ? (string) $m['deviceClientId'] : '';
|
||||||
$meas->fk_device = ($dcid !== '' && isset($deviceIdMap[$dcid])) ? $deviceIdMap[$dcid] : null;
|
$meas->fk_device = ($dcid !== '' && isset($deviceIdMap[$dcid])) ? $deviceIdMap[$dcid] : null;
|
||||||
$meas->tool = isset($m['tool']) ? (string) $m['tool'] : 'unknown';
|
// Auf die Spaltenlängen kürzen (varchar 64/32/255, siehe
|
||||||
$meas->category = isset($m['category']) ? (string) $m['category'] : '';
|
// sql/llx_netdiag_measurement.sql). Ohne das kippt bei striktem SQL-Modus
|
||||||
$meas->label = isset($m['label']) ? (string) $m['label'] : '';
|
// EIN zu langes Label den gesamten Sync: create() schlägt fehl, es folgt
|
||||||
|
// rollback() — und der Techniker steht beim Kunden mit einem Protokoll da,
|
||||||
|
// das sich nicht abschließen lässt, obwohl nur eine Beschriftung zu lang
|
||||||
|
// war. Die App setzt Labels durch Verketten zusammen (Raumname + SSID +
|
||||||
|
// Geschwindigkeit …), 255 Zeichen sind dabei erreichbar. Bei den Geräten
|
||||||
|
// wird längst so verfahren (found_via, netdiag_join_list weiter oben).
|
||||||
|
$meas->tool = substr(isset($m['tool']) ? (string) $m['tool'] : 'unknown', 0, 64);
|
||||||
|
$meas->category = substr(isset($m['category']) ? (string) $m['category'] : '', 0, 32);
|
||||||
|
$meas->label = substr(isset($m['label']) ? (string) $m['label'] : '', 0, 255);
|
||||||
$meas->params = isset($m['params']) ? json_encode($m['params'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null;
|
$meas->params = isset($m['params']) ? json_encode($m['params'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null;
|
||||||
$meas->result = isset($m['result']) ? json_encode($m['result'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null;
|
$meas->result = isset($m['result']) ? json_encode($m['result'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null;
|
||||||
// Nur bekannte Bewertungen uebernehmen. Ein unbekannter Wert landete sonst
|
// Nur bekannte Bewertungen uebernehmen. Ein unbekannter Wert landete sonst
|
||||||
|
|
|
||||||
|
|
@ -332,6 +332,16 @@ function netdiagKundenfelder()
|
||||||
'dauerSekunden' => array('Messdauer', 's'),
|
'dauerSekunden' => array('Messdauer', 's'),
|
||||||
'intervallSek' => array('Messabstand', 's'),
|
'intervallSek' => array('Messabstand', 's'),
|
||||||
'laengsterAusfallSek' => array('Längster Ausfall', 's'),
|
'laengsterAusfallSek' => array('Längster Ausfall', 's'),
|
||||||
|
// Geräte-Monitor (Dauerüberwachung). Das ist der Beleg für „das Netz
|
||||||
|
// war über X Stunden stabil" bzw. für die häufigste Reklamation
|
||||||
|
// („die Kamera fällt ständig aus") — die Felder gehören deshalb
|
||||||
|
// vollständig ins Kundendokument, nicht nur die Ampel.
|
||||||
|
'geraeteAnzahl' => array('Überwachte Geräte', ''),
|
||||||
|
'verfuegbarkeitProzent' => array('Verfügbarkeit (schlechtestes Gerät)', '%'),
|
||||||
|
'ausfallzeitSek' => array('Ausfallzeit', 's'),
|
||||||
|
'aussetzer' => array('Aussetzer', ''),
|
||||||
|
'geraete' => array('Je Gerät', ''),
|
||||||
|
'ausfaelle' => array('Einzelne Ausfälle', ''),
|
||||||
// Netz/DHCP
|
// Netz/DHCP
|
||||||
'server' => array('DHCP-Server', ''),
|
'server' => array('DHCP-Server', ''),
|
||||||
'lease' => array('Lease-Dauer', 's'),
|
'lease' => array('Lease-Dauer', 's'),
|
||||||
|
|
@ -378,14 +388,32 @@ function netdiagKundenfelder()
|
||||||
'ssid' => array('WLAN-Name', ''),
|
'ssid' => array('WLAN-Name', ''),
|
||||||
'raum' => array('Raum / Dose', ''),
|
'raum' => array('Raum / Dose', ''),
|
||||||
'iface' => array('Schnittstelle', ''),
|
'iface' => array('Schnittstelle', ''),
|
||||||
'typ' => array('Anschlussart', ''),
|
// Anschlussart. ACHTUNG: hier stand bis 16.08.2026 'typ' — ein Schlüssel,
|
||||||
|
// den kein einziges Werkzeug erzeugt. Die App liefert das LinkInfo-Objekt
|
||||||
|
// durch, und dort heißt 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. Rohwerte sind englisch und werden in
|
||||||
|
// netdiagKundentext() übersetzt.
|
||||||
|
'type' => array('Anschlussart', ''),
|
||||||
|
// Empfangspegel: bei einer WLAN-Dose IST das der Messwert. Wurde bisher
|
||||||
|
// verworfen, weil nicht in der Liste.
|
||||||
|
'rssi' => array('Empfangspegel', 'dBm'),
|
||||||
'neu' => array('Neu hinzugekommen', ''),
|
'neu' => array('Neu hinzugekommen', ''),
|
||||||
'nichtMehrErreichbar' => array('Nicht mehr erreichbar', ''),
|
'nichtMehrErreichbar' => array('Nicht mehr erreichbar', ''),
|
||||||
'demodaten' => array('DEMODATEN — keine echte Messung', ''),
|
'demodaten' => array('DEMODATEN — keine echte Messung', ''),
|
||||||
|
// Bewusst NICHT aufgenommen (Messgeräte-Interna, für den Kunden ohne
|
||||||
|
// Aussage): 'bssid', 'isUsbEthernet', 'isDefault', 'dns'.
|
||||||
// Fehler/Hinweise — immer zeigen, sonst bliebe eine gelbe oder rote Ampel
|
// Fehler/Hinweise — immer zeigen, sonst bliebe eine gelbe oder rote Ampel
|
||||||
// im Kundendokument ohne jede Erklärung stehen. Der Text läuft durch
|
// im Kundendokument ohne jede Erklärung stehen. Der Text läuft durch
|
||||||
// netdiagKundentext() und wird dort von Messgeräte-Interna befreit.
|
// netdiagKundentext() und wird dort von Messgeräte-Interna befreit.
|
||||||
'fehler' => array('Fehler', ''),
|
'fehler' => array('Fehler', ''),
|
||||||
|
// 'error' ist die englische Schreibweise desselben Feldes. Die App
|
||||||
|
// vereinheitlicht auf 'fehler', aber in bereits ausgelieferten
|
||||||
|
// Protokollen steht 'error' — und Altdaten lassen sich nicht ändern.
|
||||||
|
// Ohne diesen Eintrag stünde dort eine rote Ampel ohne einen Buchstaben
|
||||||
|
// Erklärung.
|
||||||
|
'error' => array('Fehler', ''),
|
||||||
'hinweis' => array('Hinweis', ''),
|
'hinweis' => array('Hinweis', ''),
|
||||||
'kundenhinweis' => array('Hinweis', ''),
|
'kundenhinweis' => array('Hinweis', ''),
|
||||||
);
|
);
|
||||||
|
|
@ -407,9 +435,18 @@ function netdiagDauerLesbar($sek)
|
||||||
return round($sek / 60).' min';
|
return round($sek / 60).' min';
|
||||||
}
|
}
|
||||||
if ($sek < 86400) {
|
if ($sek < 86400) {
|
||||||
return floor($sek / 3600).' Std '.round(($sek % 3600) / 60).' min';
|
$std = floor($sek / 3600);
|
||||||
|
$min = round(($sek % 3600) / 60);
|
||||||
|
// Volle Stunde nicht als „2 Std 0 min" ausgeben
|
||||||
|
return $std.' Std'.($min > 0 ? ' '.$min.' min' : '');
|
||||||
}
|
}
|
||||||
return floor($sek / 86400).' Tage '.round(($sek % 86400) / 3600).' Std';
|
// (int) nicht weglassen: floor() liefert einen Float, und 1.0 === 1 ist in
|
||||||
|
// PHP false — die Einzahl unten hätte sonst nie gegriffen.
|
||||||
|
$tage = (int) floor($sek / 86400);
|
||||||
|
$std = (int) round(($sek % 86400) / 3600);
|
||||||
|
// Einzahl/Mehrzahl und die glatte Angabe: eine DHCP-Lease von genau einem
|
||||||
|
// Tag las sich vorher als „1 Tage 0 Std".
|
||||||
|
return $tage.($tage === 1 ? ' Tag' : ' Tage').($std > 0 ? ' '.$std.' Std' : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -434,6 +471,16 @@ function netdiagFeldFuerKunde($key, $val)
|
||||||
$flat[] = is_array($item) ? json_encode($item, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : (string) $item;
|
$flat[] = is_array($item) ? json_encode($item, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : (string) $item;
|
||||||
}
|
}
|
||||||
$val = implode(', ', $flat);
|
$val = implode(', ', $flat);
|
||||||
|
// Leeres Array = das gute Ergebnis: kein offener Port, kein IP-Konflikt,
|
||||||
|
// keine Veränderung seit dem letzten Scan. Bis 16.08.2026 stand hier im
|
||||||
|
// Kundendokument „Offene Ports: " ohne Wert — die Leerprüfung weiter
|
||||||
|
// unten ist ein elseif und wurde nach diesem Zweig nie erreicht.
|
||||||
|
// „keine" statt Weglassen, weil die Zeile sonst kommentarlos
|
||||||
|
// verschwindet und der Kunde nicht unterscheiden kann, ob nichts
|
||||||
|
// gefunden oder nichts geprüft wurde.
|
||||||
|
if ($val === '') {
|
||||||
|
$val = 'keine';
|
||||||
|
}
|
||||||
} elseif (is_bool($val)) {
|
} elseif (is_bool($val)) {
|
||||||
$val = $val ? 'ja' : 'nein';
|
$val = $val ? 'ja' : 'nein';
|
||||||
} elseif ($val === null || $val === '') {
|
} elseif ($val === null || $val === '') {
|
||||||
|
|
@ -466,6 +513,21 @@ function netdiagKundentext($text)
|
||||||
if (stripos($text, '/proc/net/arp') !== false || stripos($text, 'braucht root') !== false) {
|
if (stripos($text, '/proc/net/arp') !== false || stripos($text, 'braucht root') !== false) {
|
||||||
return 'Mit dem eingesetzten Messgerät nicht prüfbar — die Aussage bleibt offen.';
|
return 'Mit dem eingesetzten Messgerät nicht prüfbar — die Aussage bleibt offen.';
|
||||||
}
|
}
|
||||||
|
// Anschlussart: Android liefert die Rohwerte englisch. Im Abnahmeprotokoll
|
||||||
|
// eines deutschen Handwerksbetriebs hat „Anschlussart: ethernet" nichts zu
|
||||||
|
// suchen. Exakter Vergleich statt stripos, damit nicht versehentlich ein
|
||||||
|
// Fließtext getroffen wird, in dem das Wort vorkommt.
|
||||||
|
$anschluss = array(
|
||||||
|
'wifi' => 'WLAN',
|
||||||
|
'ethernet' => 'LAN (Kabel)',
|
||||||
|
'cellular' => 'Mobilfunk',
|
||||||
|
'vpn' => 'VPN',
|
||||||
|
'other' => 'sonstige',
|
||||||
|
);
|
||||||
|
$k = strtolower(trim($text));
|
||||||
|
if (isset($anschluss[$k])) {
|
||||||
|
return $anschluss[$k];
|
||||||
|
}
|
||||||
return $text;
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue