prepare(' SELECT id FROM outfits WHERE image_filename = ? AND user_uuid = ? '); $stmt->execute([$filename, $userUUID]); } else { // Link visitor — image must belong to the link owner // Also respect include_private: if link is public-only, // only serve images from non-private outfits // Use a placeholder for 'private' to avoid PHP reserved-word parse issues $stmt = $pdo->prepare(" SELECT o.id FROM outfits o JOIN links l ON l.user_uuid = o.user_uuid WHERE o.image_filename = ? AND l.link_uuid = ? AND (l.include_private = 1 OR o.access_level != ?) "); $stmt->execute([$filename, $linkUUID, 'private']); } if (!$stmt->fetch()) { // Either doesn't exist or belongs to someone else — same response http_response_code(404); header('Content-Type: text/plain'); echo 'Not found'; exit; } } catch (Exception $e) { http_response_code(500); header('Content-Type: text/plain'); echo 'Server error'; exit; } // ── Serve the file ──────────────────────────────────────────── // Images live under UPLOADS_DIR// — resolve the owner UUID // from whichever auth path we took (session or link). $ownerUUID = $userUUID ?? null; if ($ownerUUID === null && $linkUUID !== null) { // For link visitors, look up the link owner's UUID try { $stmt = $pdo->prepare('SELECT user_uuid FROM links WHERE link_uuid = ?'); $stmt->execute([$linkUUID]); $link = $stmt->fetch(); $ownerUUID = $link ? $link['user_uuid'] : null; } catch (Exception $e) { $ownerUUID = null; } } if ($ownerUUID === null) { http_response_code(403); header('Content-Type: text/plain'); echo 'Forbidden'; exit; } $filePath = UPLOADS_DIR . $ownerUUID . '/' . $filename; if (!file_exists($filePath) || !is_file($filePath)) { http_response_code(404); header('Content-Type: text/plain'); echo 'Not found'; exit; } $fileSize = filesize($filePath); $etag = '"' . md5($filename . $fileSize) . '"'; // ── Cache headers — images don't change once uploaded ───────── // Short cache (5 min) so a new upload is picked up quickly, // but repeated views of the same image don't hit the server. header('Content-Type: image/jpeg'); header('Content-Length: ' . $fileSize); header('Cache-Control: private, max-age=300'); header('ETag: ' . $etag); // Honour If-None-Match for browser cache efficiency if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $etag) { http_response_code(304); exit; } http_response_code(200); readfile($filePath); exit;