db = $db; $this->settingsRepo = $settingsRepo; } public function handle(): void { if (!headers_sent()) { header('Content-Type: application/json; charset=utf-8'); } try { if (!$this->authenticate()) { self::sendError('UNAUTHORIZED', 'Invalid or missing API key', 401); return; } $endpoint = trim((string)($_GET['endpoint'] ?? '')); $action = trim((string)($_GET['action'] ?? '')); if ($endpoint === '' || $action === '') { self::sendError('BAD_REQUEST', 'Missing endpoint or action parameter', 400); return; } $controller = $this->resolveController($endpoint); if ($controller === null) { self::sendError('NOT_FOUND', 'Unknown endpoint: ' . $endpoint, 404); return; } if (!method_exists($controller, $action)) { self::sendError('NOT_FOUND', 'Unknown action: ' . $action, 404); return; } $controller->$action(); } catch (\Throwable $e) { self::sendError('INTERNAL_ERROR', 'Internal server error', 500); } } private function authenticate(): bool { $headerKey = isset($_SERVER['HTTP_X_API_KEY']) ? $_SERVER['HTTP_X_API_KEY'] : ''; if ($headerKey === '') { return false; } $storedKey = $this->settingsRepo->getSingleValue('api_key'); if ($storedKey === '') { return false; } return hash_equals($storedKey, $headerKey); } private function resolveController(string $endpoint) { $factories = $this->getControllerFactories(); if (!isset($factories[$endpoint])) { return null; } return $factories[$endpoint](); } private function getControllerFactories(): array { $db = $this->db; return [ 'orders' => function () use ($db) { $orderRepo = new \Domain\Order\OrderRepository($db); $settingsRepo = new \Domain\Settings\SettingsRepository($db); $productRepo = new \Domain\Product\ProductRepository($db); $transportRepo = new \Domain\Transport\TransportRepository($db); $cronJobRepo = new \Domain\CronJob\CronJobRepository($db); $service = new \Domain\Order\OrderAdminService($orderRepo, $productRepo, $settingsRepo, $transportRepo, $cronJobRepo); return new Controllers\OrdersApiController($service, $orderRepo); }, 'products' => function () use ($db) { $productRepo = new \Domain\Product\ProductRepository($db); $attrRepo = new \Domain\Attribute\AttributeRepository($db); return new Controllers\ProductsApiController($productRepo, $attrRepo); }, 'dictionaries' => function () use ($db) { $statusRepo = new \Domain\ShopStatus\ShopStatusRepository($db); $transportRepo = new \Domain\Transport\TransportRepository($db); $paymentRepo = new \Domain\PaymentMethod\PaymentMethodRepository($db); $attrRepo = new \Domain\Attribute\AttributeRepository($db); $producerRepo = new \Domain\Producer\ProducerRepository($db); return new Controllers\DictionariesApiController($statusRepo, $transportRepo, $paymentRepo, $attrRepo, $producerRepo); }, 'categories' => function () use ($db) { return new Controllers\CategoriesApiController(); }, ]; } // ========================================================================= // Static response helpers // ========================================================================= public static function sendSuccess($data): void { http_response_code(200); echo json_encode(['status' => 'ok', 'data' => $data], JSON_UNESCAPED_UNICODE); } public static function sendError(string $code, string $message, int $httpCode = 400): void { http_response_code($httpCode); echo json_encode([ 'status' => 'error', 'code' => $code, 'message' => $message, ], JSON_UNESCAPED_UNICODE); } public static function getJsonBody(): ?array { $raw = file_get_contents('php://input'); if ($raw === '' || $raw === false) { return null; } $data = json_decode($raw, true); return is_array($data) ? $data : null; } public static function requireMethod(string $method): bool { $requestMethod = isset($_SERVER['REQUEST_METHOD']) ? strtoupper($_SERVER['REQUEST_METHOD']) : 'GET'; if ($requestMethod !== strtoupper($method)) { self::sendError('METHOD_NOT_ALLOWED', 'Method ' . $requestMethod . ' not allowed, expected ' . strtoupper($method), 405); return false; } return true; } }