214 lines
5.9 KiB
PHP
214 lines
5.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
|
http_response_code(405);
|
|
echo json_encode([
|
|
'status' => 'error',
|
|
'message' => 'Method not allowed',
|
|
], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
$config = require __DIR__ . '/../config/menu.php';
|
|
$languages = is_array($config['languages'] ?? null) ? $config['languages'] : [];
|
|
$lang = strtolower(trim((string) ($_GET['lang'] ?? 'pl')));
|
|
|
|
if ($lang === '' || !isset($languages[$lang])) {
|
|
http_response_code(422);
|
|
echo json_encode([
|
|
'status' => 'error',
|
|
'message' => 'Unsupported language',
|
|
'allowed' => array_keys($languages),
|
|
], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
$langConfig = $languages[$lang];
|
|
$source = (string) ($langConfig['source'] ?? 'upstream');
|
|
$ttl = max(60, (int) ($config['ttl_seconds'] ?? 1800));
|
|
$timeout = max(3, (int) ($config['timeout_seconds'] ?? 12));
|
|
$cacheDir = __DIR__ . '/cache';
|
|
$cacheFile = $cacheDir . '/menu_' . $lang . '.json';
|
|
|
|
if (!is_dir($cacheDir)) {
|
|
@mkdir($cacheDir, 0755, true);
|
|
}
|
|
|
|
function readMenuCache(string $cacheFile): ?array
|
|
{
|
|
if (!is_file($cacheFile)) {
|
|
return null;
|
|
}
|
|
|
|
$raw = file_get_contents($cacheFile);
|
|
if ($raw === false || $raw === '') {
|
|
return null;
|
|
}
|
|
|
|
$decoded = json_decode($raw, true);
|
|
return is_array($decoded) ? $decoded : null;
|
|
}
|
|
|
|
function writeMenuCache(string $cacheFile, array $payload): void
|
|
{
|
|
file_put_contents(
|
|
$cacheFile,
|
|
json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
|
LOCK_EX
|
|
);
|
|
}
|
|
|
|
function fetchUpstreamMenu(string $url, int $timeout): ?array
|
|
{
|
|
$body = false;
|
|
$httpCode = 0;
|
|
|
|
if (function_exists('curl_init')) {
|
|
$ch = curl_init($url);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_FOLLOWLOCATION => true,
|
|
CURLOPT_TIMEOUT => $timeout,
|
|
CURLOPT_HTTPHEADER => ['Accept: application/json'],
|
|
]);
|
|
$body = curl_exec($ch);
|
|
$errno = curl_errno($ch);
|
|
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
if ($errno !== 0) {
|
|
$body = false;
|
|
}
|
|
} else {
|
|
$context = stream_context_create([
|
|
'http' => [
|
|
'method' => 'GET',
|
|
'timeout' => $timeout,
|
|
'header' => "Accept: application/json\r\n",
|
|
'ignore_errors' => true,
|
|
],
|
|
]);
|
|
$body = @file_get_contents($url, false, $context);
|
|
if (isset($http_response_header[0]) && preg_match('/\s(\d{3})\s/', $http_response_header[0], $m)) {
|
|
$httpCode = (int) $m[1];
|
|
}
|
|
}
|
|
|
|
if ($body === false || ($httpCode > 0 && ($httpCode < 200 || $httpCode >= 300))) {
|
|
return null;
|
|
}
|
|
|
|
$decoded = json_decode($body, true);
|
|
return is_array($decoded) ? $decoded : null;
|
|
}
|
|
|
|
function mapUpstreamMenu(array $raw, string $lang, string $source): array
|
|
{
|
|
$categories = [];
|
|
foreach ($raw['categories'] ?? [] as $cat) {
|
|
if (!is_array($cat)) {
|
|
continue;
|
|
}
|
|
$id = trim((string) ($cat['id'] ?? ''));
|
|
$name = trim((string) ($cat['name'] ?? ''));
|
|
if ($id === '' || $name === '') {
|
|
continue;
|
|
}
|
|
$categories[] = ['id' => $id, 'name' => $name];
|
|
}
|
|
|
|
$sections = [];
|
|
foreach ($raw['tables'] ?? [] as $table) {
|
|
if (!is_array($table)) {
|
|
continue;
|
|
}
|
|
$sectionName = trim((string) ($table['name'] ?? ''));
|
|
if ($sectionName === '') {
|
|
continue;
|
|
}
|
|
|
|
$items = [];
|
|
foreach ($table['items'] ?? [] as $item) {
|
|
if (!is_array($item)) {
|
|
continue;
|
|
}
|
|
$items[] = [
|
|
'position' => (string) ($item['id'] ?? ''),
|
|
'categoryId' => (string) ($item['category'] ?? ''),
|
|
'tag' => '',
|
|
'image' => (string) ($item['image'] ?? ''),
|
|
'title' => trim((string) ($item['name'] ?? '')),
|
|
'description' => trim((string) ($item['name_extra'] ?? '')),
|
|
'price' => (string) ($item['price'] ?? ''),
|
|
];
|
|
}
|
|
|
|
$sections[] = [
|
|
'categoryName' => $sectionName,
|
|
'items' => $items,
|
|
];
|
|
}
|
|
|
|
return [
|
|
'status' => 'success',
|
|
'lang' => $lang,
|
|
'source' => $source,
|
|
'cachedAt' => gmdate('c'),
|
|
'categories' => $categories,
|
|
'sections' => $sections,
|
|
];
|
|
}
|
|
|
|
$cached = readMenuCache($cacheFile);
|
|
$now = time();
|
|
$cachedAtTs = 0;
|
|
if ($cached && !empty($cached['cachedAt'])) {
|
|
$cachedAtTs = strtotime((string) $cached['cachedAt']) ?: 0;
|
|
}
|
|
$isFresh = $cached && $cachedAtTs > 0 && ($now - $cachedAtTs) < $ttl;
|
|
|
|
if ($isFresh) {
|
|
echo json_encode($cached, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
exit;
|
|
}
|
|
|
|
if ($source === 'ai') {
|
|
if ($cached) {
|
|
echo json_encode($cached, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
exit;
|
|
}
|
|
http_response_code(503);
|
|
echo json_encode([
|
|
'status' => 'error',
|
|
'message' => 'AI menu not available yet',
|
|
'lang' => $lang,
|
|
], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
$base = rtrim((string) ($config['upstream_base'] ?? ''), '/');
|
|
$url = $base . '/' . rawurlencode($lang);
|
|
$raw = fetchUpstreamMenu($url, $timeout);
|
|
|
|
if ($raw === null) {
|
|
if ($cached) {
|
|
echo json_encode($cached, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
exit;
|
|
}
|
|
http_response_code(502);
|
|
echo json_encode([
|
|
'status' => 'error',
|
|
'message' => 'Failed to fetch menu',
|
|
'lang' => $lang,
|
|
], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
$mapped = mapUpstreamMenu($raw, $lang, 'upstream');
|
|
writeMenuCache($cacheFile, $mapped);
|
|
|
|
echo json_encode($mapped, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|