" — this lets // us drop 'unsafe-inline' from script-src. $cspNonce = base64_encode(random_bytes(16)); header("Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{$cspNonce}' https://cdn.jsdelivr.net https://www.tiktok.com https://www.instagram.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdn.jsdelivr.net; font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net; img-src 'self' data: blob: https://www.smilingpages.com https://commons.wikimedia.org https://upload.wikimedia.org https://www.themealdb.com https://www.thecocktaildb.com https://i.ytimg.com https://img.youtube.com https://www.tiktok.com https://*.cdninstagram.com https://*.fbcdn.net https://images.unsplash.com https://i.imgur.com https://lh3.googleusercontent.com; frame-src https://www.youtube.com https://www.youtube-nocookie.com https://www.tiktok.com https://www.instagram.com; connect-src 'self' https://cdn.jsdelivr.net https://api.mymemory.translated.net https://translate.googleapis.com https://ip-api.com; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self';"); $GLOBALS['csp_nonce'] = $cspNonce; spl_autoload_register(function (string $class): void { $path = ROOT . '/src/' . str_replace('\\', '/', $class) . '.php'; if (file_exists($path)) require_once $path; }); // Template-visible aliases (templates are plain PHP files outside any namespace) class_alias('Core\View', 'View'); class_alias('Core\Lang', 'Lang'); // Multi-language — detect and load Core\Lang::detect(); Core\Lang::load(); $router = new Core\Router(); // ── Pages ──────────────────────────────────────────────────────────────────── $router->get('/', [Controllers\HomeController::class, 'index']); $router->get('/recipe/{slug}', [Controllers\RecipeController::class, 'show']); $router->get('/category/{slug}', [Controllers\CategoryController::class, 'index']); $router->get('/category/{slug}/page/{page}', [Controllers\CategoryController::class, 'index']); $router->get('/tag/{slug}', [Controllers\TagController::class, 'index']); $router->get('/tag/{slug}/page/{page}', [Controllers\TagController::class, 'index']); $router->get('/search', [Controllers\SearchController::class, 'index']); $router->get('/trending', [Controllers\TrendingController::class, 'index']); $router->get('/trending/page/{page}', [Controllers\TrendingController::class, 'index']); $router->get('/recipe-of-the-day', [Controllers\RecipeController::class, 'ofTheDay']); $router->get('/privacy-policy', [Controllers\PageController::class, 'privacy']); $router->get('/pantry', [Controllers\PageController::class, 'pantry']); $router->get('/globe', [Controllers\PageController::class, 'globe']); $router->get('/markets', [Controllers\PageController::class, 'markets']); // ── Sitemaps ───────────────────────────────────────────────────────────────── $router->get('/sitemap_index.xml', [Controllers\SitemapController::class, 'index']); $router->get('/sitemap_recipes_{n}.xml', [Controllers\SitemapController::class, 'recipes']); $router->get('/sitemap_categories.xml', [Controllers\SitemapController::class, 'categories']); $router->get('/sitemap_images.xml', [Controllers\SitemapController::class, 'imagesIndex']); $router->get('/sitemap_images_{n}.xml', [Controllers\SitemapController::class, 'images']); $router->get('/sitemap_videos.xml', [Controllers\SitemapController::class, 'videos']); // ── API ─────────────────────────────────────────────────────────────────────── $router->post('/api/vote', [Controllers\ApiController::class, 'vote']); $router->post('/api/comment', [Controllers\ApiController::class, 'comment']); $router->get('/api/search', [Controllers\ApiController::class, 'search']); $router->get('/api/trending', [Controllers\ApiController::class, 'trending']); $router->get('/api/recipe/{slug}', [Controllers\ApiController::class, 'recipe']); $router->post('/api/pantry-match', [Controllers\ApiController::class, 'pantryMatch']); $router->get('/api/globe-recipes', [Controllers\ApiController::class, 'globeRecipes']); $router->get('/api/captcha', [Controllers\ApiController::class, 'captcha']); $router->get('/api/geocode', [Controllers\ApiController::class, 'geocode']); $router->get('/api/markets', [Controllers\ApiController::class, 'markets']); // ── Image generation ────────────────────────────────────────────────────────── $router->get('/api/recipe-image', function() { require ROOT . '/api/recipe-image.php'; }); $router->get('/api/og-image', function() { require ROOT . '/api/og-image.php'; }); // ── Dispatch ───────────────────────────────────────────────────────────────── $method = $_SERVER['REQUEST_METHOD']; $uri = $_SERVER['REQUEST_URI']; $router->dispatch($method, $uri);