feat: api/photo.php — Foto-Auslieferung mit JWT-Auth für PWA
All checks were successful
Deploy bericht / deploy (push) Successful in 1s

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) <noreply@anthropic.com>
[deploy]
This commit is contained in:
Eduard Wisch 2026-04-08 23:18:08 +02:00
parent 3069453823
commit 95701ed2d6

52
api/photo.php Normal file
View file

@ -0,0 +1,52 @@
<?php
/* GET /api/photo.php?relpath=<path>
* 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 <name>_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;