Files
portfoli-ulleres/api/products.php
T

182 lines
6.0 KiB
PHP

<?php
declare(strict_types=1);
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
$googleScriptUrl = 'https://script.googleusercontent.com/macros/echo?user_content_key=AWDtjMVe9JQMYFUTitGGiHSzQsuEyr7VsarNnNpOluFcWHXa-CGnuKhrinmwBYVLw1otHQoBg5rnYNpGlIvCg_I8u1QhCKr-FQwCC2bG9LdttpST6nS_k8TxRcaT5LmmDjeZENcy8A0ujTU1yJwFLoxudMFW-OGjkYtywE5YT_CUJpKQWmqPN8IRV1drNysBtQFfQH1tXUS1JrOODghrxxjAA3T77kqRnz-aaMZJ23YfZntVm4C1KpaBBloFs4OO2wYIcD7Sf1iAYX1OMjIHXsCypvIgFRfIdhMqADWRmluv&lib=MLri0H8XjzrQY2XIy3BzwJ1YbTZr7i_Wa';
$cacheDir = __DIR__ . '/cache';
$cacheFile = $cacheDir . '/products.json';
$cacheTtl = 120; // segons
function respond(int $statusCode, array $payload): void
{
http_response_code($statusCode);
echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
exit;
}
function fetchRemoteJson(string $url): array
{
if (!function_exists('curl_init')) {
throw new RuntimeException('cURL no està disponible al PHP de la Synology.');
}
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => 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, ',', '.') . ' €';
}
$image = normalizeText($p['image_url'] ?? $p['IMAGE_URL'] ?? '');
$filename = basename($image);
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' => $filename ? "https://kapvoe-portfoli.treblarella.org/assets/products/$filename" : '',
'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()
]);
}