<?php
declare(strict_types=1);
if (!defined('STORE_ROOT')) { http_response_code(403); exit; }
function pageHandler(string $action, array $in): void { App::requireAdmin(); try { DB::run("CREATE TABLE IF NOT EXISTS page_blocks (             id INT AUTO_INCREMENT PRIMARY KEY,             store_id INT NOT NULL DEFAULT 1,             page VARCHAR(32) NOT NULL DEFAULT 'home',             block_key VARCHAR(64) NOT NULL,             block_type VARCHAR(32) NOT NULL DEFAULT 'generic',             content TEXT NOT NULL,             sort_order INT NOT NULL DEFAULT 0,             is_active TINYINT(1) NOT NULL DEFAULT 1,             updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,             UNIQUE KEY uq_block (store_id, page, block_key)         ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"); } catch (\Throwable $e) {} $csrfHeader = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? ''; $csrfSession = $_SESSION['csrf_token'] ?? ''; if ($action === 'save' && (!$csrfHeader || !hash_equals($csrfSession, $csrfHeader))) { App::err('Invalid CSRF token', 403); } if ($action === 'save') { $storeId = (int) ($in['store_id'] ?? 1); $page = preg_replace('/[^a-z0-9_]/', '', $in['page'] ?? 'home'); $blockKey = preg_replace('/[^a-z0-9_]/', '', $in['block_key'] ?? ''); $blockType = preg_replace('/[^a-z0-9_]/', '', $in['block_type'] ?? 'generic'); $sortOrder = (int) ($in['sort_order'] ?? 0); $contentRaw = $in['content'] ?? ''; if (!$blockKey) App::err('block_key required'); $parsed = is_array($contentRaw) ? $contentRaw : json_decode((string) $contentRaw, true); if ($parsed === null) App::err('Invalid JSON content'); $enc = Crypt::enc(json_encode($parsed, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), 'default'); DB::run( "INSERT INTO page_blocks (store_id, page, block_key, block_type, content, sort_order, is_active)              VALUES (:sid, :page, :bk, :bt, :c, :so, 1)              ON DUPLICATE KEY UPDATE content = :c2, block_type = :bt2, sort_order = :so2, is_active = 1, updated_at = NOW()", [':sid' => $storeId, ':page' => $page, ':bk' => $blockKey, ':bt' => $blockType, ':c' => $enc, ':so' => $sortOrder, ':c2' => $enc, ':bt2' => $blockType, ':so2' => $sortOrder] ); $verify = DB::one("SELECT content FROM page_blocks WHERE store_id = :sid AND page = :page AND block_key = :bk", [':sid' => $storeId, ':page' => $page, ':bk' => $blockKey]); $verified = $verify ? json_decode(Crypt::d($verify['content']), true) : null; App::ok(['block_key' => $blockKey, 'verified' => $verified]); } if ($action === 'load') { $storeId = (int) ($in['store_id'] ?? 1); $page = preg_replace('/[^a-z0-9_]/', '', $in['page'] ?? 'home'); $blockKey = preg_replace('/[^a-z0-9_]/', '', $in['block_key'] ?? ''); if (!$blockKey) App::err('block_key required'); $row = DB::one("SELECT content, block_type FROM page_blocks WHERE store_id = :sid AND page = :page AND block_key = :bk AND is_active = 1", [':sid' => $storeId, ':page' => $page, ':bk' => $blockKey]); if (!$row) App::ok(['content' => null]); $dec = Crypt::d($row['content']); App::ok(['block_type' => $row['block_type'], 'content' => json_decode($dec, true) ?? $dec]); } App::err('Unknown page action'); }
