.png // → si cache fresh (< 6h) → serve PNG // → sinon → background trigger Playwright script → serve placeholder OR queue // → fallback 404 → return 204 empty PNG (so img tag shows nothing, not broken) // ============================================================ // SAFETY : read-only shell_exec with hash, zéro injection, whitelist paths, cache 6h // ============================================================ header('Cache-Control: public, max-age=21600'); // 6h browser cache too $path = $_GET['path'] ?? ''; if (!$path || strlen($path) > 250) { http_response_code(400); exit; } // Security: path must be safe (alnum + dash + dot + slash + underscore only) if (!preg_match('/^[a-zA-Z0-9\/_\-\.]+$/', $path)) { http_response_code(400); exit; } // Reject path traversal if (strpos($path, '..') !== false) { http_response_code(400); exit; } $path = ltrim($path, '/'); // Detect domain prefix (wevads/, ethica/) → build full URL for worker $worker_arg = $path; if (strpos($path, 'wevads/') === 0) { $worker_arg = 'https://wevads.weval-consulting.com/' . substr($path, 7); } elseif (strpos($path, 'ethica/') === 0) { $worker_arg = 'https://ethica.wevup.app/' . substr($path, 7); } // else: bare path → worker treats as S204-local // Hash uses the key (= path with prefix) so it matches worker $hash = md5($path); $thumb = "/var/www/html/api/screenshots/wem/{$hash}.png"; $now = time(); $ttl = 21600; // 6h $fresh = file_exists($thumb) && ($now - filemtime($thumb)) < $ttl; if ($fresh) { header('Content-Type: image/png'); header('X-Cache: HIT'); header('X-Thumb-Age: ' . ($now - filemtime($thumb))); readfile($thumb); exit; } // Cache miss OR stale — trigger background generation, return placeholder or old cache $exists_stale = file_exists($thumb); $lockfile = "/tmp/wem-thumb-{$hash}.lock"; // Only trigger if no lock (prevent stampede) if (!file_exists($lockfile) || (time() - filemtime($lockfile)) > 120) { @touch($lockfile); // Fire-and-forget Playwright capture (worker handles URL resolution) $cmd = "nohup python3 /var/www/html/api/wem-thumb-worker.py " . escapeshellarg($worker_arg) . " > /tmp/wem-thumb.log 2>&1 &"; @shell_exec($cmd); } if ($exists_stale) { // Serve stale version while fresh one generates header('Content-Type: image/png'); header('X-Cache: STALE-REFRESHING'); readfile($thumb); exit; } // No cache at all — serve 1x1 transparent PNG placeholder (tile will show skeleton) header('Content-Type: image/png'); header('X-Cache: MISS-QUEUED'); // 1x1 transparent PNG echo base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=');