dolibarr.netdiag/api/protocols.php
Eduard Wisch db2390d997
All checks were successful
Deploy netdiag / deploy (push) Successful in 16s
Sync-500 behoben: App-Zeitstempel (ms) auf Sekunden umrechnen [deploy]
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

220 lines
8.5 KiB
PHP

<?php
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* \file netdiag/api/protocols.php
* \ingroup netdiag
* \brief API-Endpunkt: Protokolle lesen (GET ?id=) und synchronisieren
* (POST action=sync). Der Sync ist idempotent über client_uuid.
*/
require_once __DIR__.'/netdiag_api.lib.php';
netdiag_api_bootstrap();
/**
* @var Conf $conf
* @var DoliDB $db
* @var Translate $langs
*/
$user = netdiag_api_authenticate($db);
require_once __DIR__.'/../class/netdiagprotocol.class.php';
require_once __DIR__.'/../class/netdiagdevice.class.php';
require_once __DIR__.'/../class/netdiagmeasurement.class.php';
// =========================================================================
// GET: einzelnes Protokoll mit Geräten und Messungen
// =========================================================================
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
if ($id <= 0) {
netdiag_api_error('Parameter id fehlt', 400);
}
$protocol = new NetDiagProtocol($db);
if ($protocol->fetch($id) <= 0) {
netdiag_api_error('Protokoll nicht gefunden', 404);
}
$devObj = new NetDiagDevice($db);
$devices = array();
foreach ($devObj->fetchAllByProtocol($protocol->id) as $d) {
$devices[] = array(
'id' => (int) $d->id,
'ip' => $d->ip,
'mac' => $d->mac,
'hostname' => $d->hostname,
'vendor' => $d->vendor,
'deviceType' => $d->devicetype,
'note' => $d->note,
);
}
$measObj = new NetDiagMeasurement($db);
$measurements = array();
foreach ($measObj->fetchAllByProtocol($protocol->id) as $m) {
$measurements[] = array(
'id' => (int) $m->id,
'deviceId' => $m->fk_device ? (int) $m->fk_device : null,
'tool' => $m->tool,
'category' => $m->category,
'label' => $m->label,
'params' => $m->params ? json_decode($m->params, true) : null,
'result' => $m->result ? json_decode($m->result, true) : null,
'measureStatus' => (int) $m->measure_status,
'dateMeasure' => $db->jdate($m->date_measure),
);
}
netdiag_api_respond(array(
'protocol' => array(
'id' => (int) $protocol->id,
'ref' => $protocol->ref,
'label' => $protocol->label,
'clientUuid' => $protocol->client_uuid,
'socId' => $protocol->fk_soc ? (int) $protocol->fk_soc : null,
'orderId' => $protocol->fk_commande ? (int) $protocol->fk_commande : null,
'dateDiag' => $db->jdate($protocol->date_diag),
'location' => $protocol->standort,
'subnet' => $protocol->subnet,
'status' => (int) $protocol->status,
'note' => $protocol->note,
),
'devices' => $devices,
'measurements' => $measurements,
));
}
// =========================================================================
// POST: Protokoll synchronisieren (anlegen oder aktualisieren)
// =========================================================================
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
netdiag_api_error('Methode nicht erlaubt', 405);
}
if (!$user->hasRight('netdiag', 'protocol', 'write')) {
netdiag_api_error('Keine Schreibberechtigung', 403);
}
$body = netdiag_api_read_body();
if (($body['action'] ?? '') !== 'sync' || empty($body['protocol']) || !is_array($body['protocol'])) {
netdiag_api_error('Erwartet: { action: "sync", protocol: {...} }', 400);
}
$p = $body['protocol'];
$uuid = isset($p['clientUuid']) ? trim((string) $p['clientUuid']) : '';
if ($uuid === '') {
netdiag_api_error('protocol.clientUuid erforderlich', 400);
}
$db->begin();
// Vorhandenes Protokoll über UUID suchen (idempotent)
$protocol = new NetDiagProtocol($db);
$exists = $protocol->fetchByClientUuid($uuid);
if ($exists < 0) {
$db->rollback();
netdiag_api_error('Datenbankfehler beim Laden', 500);
}
$protocol->client_uuid = $uuid;
$protocol->label = isset($p['label']) ? (string) $p['label'] : '';
$protocol->fk_soc = !empty($p['socId']) ? (int) $p['socId'] : null;
$protocol->fk_commande = !empty($p['orderId']) ? (int) $p['orderId'] : null;
$protocol->date_diag = netdiag_api_timestamp($p['dateDiag'] ?? 0);
$protocol->fk_user_techniker = (int) $user->id;
$protocol->standort = isset($p['location']) ? (string) $p['location'] : '';
$protocol->subnet = isset($p['subnet']) ? (string) $p['subnet'] : '';
$protocol->status = isset($p['status']) ? (int) $p['status'] : NetDiagProtocol::STATUS_DRAFT;
$protocol->note = isset($p['note']) ? (string) $p['note'] : '';
// tms explizit setzen — die Spalte ist NOT NULL ohne brauchbaren NULL-Default
// (explicit_defaults_for_timestamp=1), createCommon würde sonst NULL einfügen.
$protocol->tms = dol_now();
if ($exists > 0) {
$result = $protocol->update($user, 1);
} else {
$result = $protocol->create($user, 1);
}
if ($result <= 0) {
$db->rollback();
netdiag_api_error('Protokoll speichern fehlgeschlagen: '.$protocol->errorsToString(), 500);
}
$protocolId = (int) $protocol->id;
// Alte Geräte und Messungen entfernen (Sync ersetzt komplett)
$db->query("DELETE FROM ".$db->prefix()."netdiag_measurement WHERE fk_protocol = ".$protocolId);
$db->query("DELETE FROM ".$db->prefix()."netdiag_device WHERE fk_protocol = ".$protocolId);
// Geräte einfügen, dabei clientId -> serverRowid merken
$deviceIdMap = array();
$devicesIn = (!empty($p['devices']) && is_array($p['devices'])) ? $p['devices'] : array();
foreach ($devicesIn as $d) {
$dev = new NetDiagDevice($db);
$dev->fk_protocol = $protocolId;
$dev->ip = isset($d['ip']) ? (string) $d['ip'] : '';
$dev->mac = isset($d['mac']) ? (string) $d['mac'] : '';
$dev->hostname = isset($d['hostname']) ? (string) $d['hostname'] : '';
$dev->vendor = isset($d['vendor']) ? (string) $d['vendor'] : '';
$dev->devicetype = isset($d['deviceType']) ? (string) $d['deviceType'] : '';
$dev->note = isset($d['note']) ? (string) $d['note'] : '';
$dev->tms = dol_now();
if ($dev->create($user, 1) <= 0) {
$db->rollback();
netdiag_api_error('Gerät speichern fehlgeschlagen: '.$dev->errorsToString(), 500);
}
if (isset($d['clientId'])) {
$deviceIdMap[(string) $d['clientId']] = (int) $dev->id;
}
}
// Messungen einfügen, deviceClientId auf serverRowid abbilden
$measIn = (!empty($p['measurements']) && is_array($p['measurements'])) ? $p['measurements'] : array();
foreach ($measIn as $m) {
$meas = new NetDiagMeasurement($db);
$meas->fk_protocol = $protocolId;
$dcid = isset($m['deviceClientId']) ? (string) $m['deviceClientId'] : '';
$meas->fk_device = ($dcid !== '' && isset($deviceIdMap[$dcid])) ? $deviceIdMap[$dcid] : null;
$meas->tool = isset($m['tool']) ? (string) $m['tool'] : 'unknown';
$meas->category = isset($m['category']) ? (string) $m['category'] : '';
$meas->label = isset($m['label']) ? (string) $m['label'] : '';
$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->measure_status = isset($m['measureStatus']) ? (int) $m['measureStatus'] : 0;
$meas->date_measure = netdiag_api_timestamp($m['dateMeasure'] ?? 0);
$meas->tms = dol_now();
if ($meas->create($user, 1) <= 0) {
$db->rollback();
netdiag_api_error('Messung speichern fehlgeschlagen: '.$meas->errorsToString(), 500);
}
}
$db->commit();
// PDF neu erzeugen, wenn das Protokoll abgeschlossen ist
$pdfgenerated = false;
if ($protocol->status == NetDiagProtocol::STATUS_DONE) {
require_once __DIR__.'/../lib/netdiag_pdf.lib.php';
$pdfgenerated = (netdiagGeneratePdf($db, $protocol, $langs) > 0);
}
netdiag_api_respond(array(
'ok' => true,
'protocolId' => $protocolId,
'ref' => $protocol->ref,
'created' => ($exists == 0),
'pdfGenerated' => $pdfgenerated,
));