From 7d0fea670fb175737996ab2313b6a11bdb5f653c Mon Sep 17 00:00:00 2001 From: Albert Date: Wed, 8 Apr 2026 11:52:52 +0200 Subject: [PATCH] Frontend connectat a API PHP en produccio --- api/products.php | 179 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 api/products.php diff --git a/api/products.php b/api/products.php new file mode 100644 index 0000000..6c18591 --- /dev/null +++ b/api/products.php @@ -0,0 +1,179 @@ + true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_TIMEOUT => 20, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_HTTPHEADER => [ + 'Accept: application/json', + 'User-Agent: KapvoePortfolioAPI/1.0' + ], + CURLOPT_SSL_VERIFYPEER => true, + CURLOPT_SSL_VERIFYHOST => 2, + ]); + + $response = curl_exec($ch); + + if ($response === false) { + $error = curl_error($ch); + curl_close($ch); + throw new RuntimeException("Error cURL: {$error}"); + } + + $statusCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($statusCode < 200 || $statusCode >= 300) { + throw new RuntimeException("Resposta remota no vàlida. HTTP {$statusCode}"); + } + + $json = json_decode($response, true); + + if (!is_array($json)) { + throw new RuntimeException('La resposta remota no és JSON vàlid.'); + } + + return $json; +} + +function normalizeBool($value): bool +{ + $v = mb_strtolower(trim((string)($value ?? ''))); + return in_array($v, ['1', 'true', 'sí', 'si', 'yes', 'y'], true); +} + +function normalizeText($value): string +{ + return trim((string)($value ?? '')); +} + +function normalizePriceNumber($value): ?float +{ + if ($value === null || $value === '') { + return null; + } + + $raw = str_replace(['€', ' '], '', (string)$value); + $raw = str_replace(',', '.', $raw); + + return is_numeric($raw) ? (float)$raw : null; +} + +function normalizeStock($value): int +{ + if ($value === null || $value === '') { + return 0; + } + + return max(0, (int)$value); +} + +function normalizeProduct(array $p): array +{ + $priceNumber = normalizePriceNumber($p['europe_price_number'] ?? $p['PREU'] ?? $p['PREU EUROPA'] ?? null); + $priceText = normalizeText($p['europe_price_text'] ?? ''); + + if ($priceText === '' && $priceNumber !== null) { + $priceText = number_format($priceNumber, 2, ',', '.') . ' €'; + } + + return [ + 'product_code' => normalizeText($p['product_code'] ?? $p['Product Code'] ?? $p['PRODUCT CODE'] ?? ''), + 'model' => normalizeText($p['model'] ?? $p['MODEL'] ?? ''), + 'category' => normalizeText($p['category'] ?? $p['CATEGORIA'] ?? $p['CATEGORY'] ?? ''), + 'colors' => normalizeText($p['colors'] ?? $p['COLORS'] ?? $p['VIDRE'] ?? ''), + 'description' => normalizeText($p['description'] ?? $p['DESCRIPCIO'] ?? $p['DESCRIPTION'] ?? ''), + 'image_url' => normalizeText($p['image_url'] ?? $p['IMAGE_URL'] ?? ''), + 'stock' => normalizeStock($p['stock'] ?? $p['STOCK'] ?? 0), + 'top_vendes' => normalizeBool($p['top_vendes'] ?? $p['TOP_VENDES'] ?? false), + 'europe_price_number' => $priceNumber, + 'europe_price_text' => $priceText + ]; +} + +try { + if (!is_dir($cacheDir)) { + mkdir($cacheDir, 0775, true); + } + + $forceRefresh = isset($_GET['refresh']) && $_GET['refresh'] === '1'; + $useCache = !$forceRefresh && file_exists($cacheFile) && (time() - filemtime($cacheFile) < $cacheTtl); + + if ($useCache) { + $cached = json_decode((string)file_get_contents($cacheFile), true); + if (is_array($cached)) { + respond(200, $cached); + } + } + + if ($googleScriptUrl === 'POSA_AQUI_LA_TEVA_URL_DE_GOOGLE_SCRIPT') { + throw new RuntimeException('Has de configurar la URL del Google Script a api/products.php'); + } + + $remote = fetchRemoteJson($googleScriptUrl); + + if (($remote['ok'] ?? null) !== true || !isset($remote['products']) || !is_array($remote['products'])) { + throw new RuntimeException('El JSON remot no té el format esperat.'); + } + + $normalizedProducts = array_map('normalizeProduct', $remote['products']); + + $normalizedProducts = array_values(array_filter($normalizedProducts, function (array $p): bool { + return $p['product_code'] !== ''; + })); + + $payload = [ + 'ok' => true, + 'source' => 'php-proxy', + 'cached' => false, + 'updated_at' => $remote['updated_at'] ?? date('c'), + 'count' => count($normalizedProducts), + 'products' => $normalizedProducts + ]; + + file_put_contents($cacheFile, json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + respond(200, $payload); + +} catch (Throwable $e) { + if (file_exists($cacheFile)) { + $cached = json_decode((string)file_get_contents($cacheFile), true); + if (is_array($cached)) { + $cached['cached'] = true; + $cached['warning'] = 'S\'està retornant cache perquè la font remota ha fallat.'; + $cached['error_detail'] = $e->getMessage(); + respond(200, $cached); + } + } + + respond(500, [ + 'ok' => false, + 'error' => 'No s\'ha pogut obtenir el catàleg.', + 'detail' => $e->getMessage() + ]); +} \ No newline at end of file