From 95701ed2d65128336677644600ef547bbbd1b2d6 Mon Sep 17 00:00:00 2001 From: Eduard Wisch Date: Wed, 8 Apr 2026 23:18:08 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20api/photo.php=20=E2=80=94=20Foto-Auslie?= =?UTF-8?q?ferung=20mit=20JWT-Auth=20f=C3=BCr=20PWA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neuer Endpoint liefert Dateien aus DOL_DATA_ROOT aus, geschützt per JWT statt Dolibarr-Session. Whitelist auf facture/commande/propal/ bericht. Optional size=small/mini für Thumbs (Dolibarr _small Variante). CORS-Header damit PWA direkt zugreifen kann. Die PWA kann damit Anhang-Bilder in der Auftrags-Detail-Ansicht rendern — vorher nutzte sie document.php was nur mit Session ging und deshalb im Standalone-Mode leer blieb. Co-Authored-By: Claude Opus 4.6 (1M context) [deploy] --- api/photo.php | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 api/photo.php diff --git a/api/photo.php b/api/photo.php new file mode 100644 index 0000000..36e1f8a --- /dev/null +++ b/api/photo.php @@ -0,0 +1,52 @@ + + * Liefert eine Datei aus DOL_DATA_ROOT aus, authentifiziert per JWT. + * Whitelist: nur facture/, commande/, propal/, bericht/ + * + * Für den Thumb-Request der PWA werden auch Thumbnails ausgeliefert + * (Dolibarr legt _small.png unter thumbs/ ab). + * + * Query: + * relpath — relativer Pfad unter DOL_DATA_ROOT + * size=small|mini (optional, nutzt automatisch das Thumb) + */ +require_once __DIR__.'/_inc.php'; + +api_authenticate(); +global $db, $user; + +$relpath = (string) ($_GET['relpath'] ?? ''); +$size = (string) ($_GET['size'] ?? ''); + +if (empty($relpath)) api_fail('relpath fehlt'); + +// Whitelist +if (!preg_match('#^(facture|commande|propal|bericht)/#', $relpath)) { + api_fail('Pfad nicht erlaubt', 403); +} + +$full = bericht_resolve_data_path($relpath); +if (!$full || !file_exists($full)) api_fail('Datei nicht gefunden', 404); + +// Thumb-Variante (kleiner, schneller) +if ($size === 'small' || $size === 'mini') { + $dir = dirname($full); + $base = pathinfo($full, PATHINFO_FILENAME); + $ext = pathinfo($full, PATHINFO_EXTENSION); + $thumb = $dir.'/thumbs/'.$base.'_'.$size.'.'.$ext; + if (file_exists($thumb)) { + $full = $thumb; + } +} + +// Content-Type aus mimetype +$mime = function_exists('dol_mimetype') ? dol_mimetype($full) : 'application/octet-stream'; + +// Binary ausliefern — header() ersetzt vorherigen JSON Content-Type +header_remove('Content-Type'); +header('Content-Type: '.$mime); +header('Content-Length: '.filesize($full)); +header('Cache-Control: private, max-age=3600'); +header('Access-Control-Allow-Origin: *'); +readfile($full); +exit;