Frontend connectat a API PHP en produccio
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
<?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, ',', '.') . ' €';
|
||||
}
|
||||
|
||||
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()
|
||||
]);
|
||||
}
|
||||
Reference in New Issue
Block a user