1 | <?php |
||
24 | final class ApiPresenter implements IPresenter |
||
25 | { |
||
26 | /** @var ApiDecider @inject */ |
||
27 | public $apiDecider; |
||
28 | |||
29 | /** @var Response @inject */ |
||
30 | public $response; |
||
31 | |||
32 | /** @var Container @inject */ |
||
33 | public $context; |
||
34 | |||
35 | /** |
||
36 | * CORS header settings |
||
37 | * |
||
38 | * Available values: |
||
39 | * 'auto' - send back header Access-Control-Allow-Origin with domain that made request |
||
40 | * '*' - send header with '*' - this will workf fine if you dont need to send cookies via ajax calls to api |
||
41 | * with jquery $.ajax with xhrFields: { withCredentials: true } settings |
||
42 | * 'off' - will not send any CORS header |
||
43 | * other - any other value will be send in Access-Control-Allow-Origin header |
||
44 | * |
||
45 | * @var string |
||
46 | */ |
||
47 | protected $corsHeader = '*'; |
||
48 | |||
49 | /** |
||
50 | * Set cors header |
||
51 | * |
||
52 | * See description to property $corsHeader for valid inputs |
||
53 | * |
||
54 | * @param string $corsHeader |
||
55 | */ |
||
56 | public function setCorsHeader(string $corsHeader): void |
||
60 | |||
61 | 12 | public function run(Request $request): IResponse |
|
62 | { |
||
63 | 12 | $start = microtime(true); |
|
64 | |||
65 | 12 | $this->sendCorsHeaders(); |
|
66 | |||
67 | 12 | $api = $this->getApi($request); |
|
68 | 12 | $handler = $api->getHandler(); |
|
69 | 12 | $authorization = $api->getAuthorization(); |
|
70 | 12 | $rateLimit = $api->getRateLimit(); |
|
71 | |||
72 | 12 | $authResponse = $this->checkAuth($authorization); |
|
73 | 12 | if ($authResponse !== null) { |
|
74 | 3 | return $authResponse; |
|
75 | } |
||
76 | |||
77 | 9 | $rateLimitResponse = $this->checkRateLimit($rateLimit); |
|
78 | 9 | if ($rateLimitResponse !== null) { |
|
79 | return $rateLimitResponse; |
||
80 | } |
||
81 | |||
82 | 9 | $paramsProcessor = new ParamsProcessor($handler->params()); |
|
83 | 9 | if ($paramsProcessor->isError()) { |
|
84 | 3 | $this->response->setCode(Response::S400_BAD_REQUEST); |
|
85 | 3 | if (!Debugger::$productionMode) { |
|
86 | 3 | $response = new JsonResponse(['status' => 'error', 'message' => 'wrong input', 'detail' => $paramsProcessor->getErrors()]); |
|
87 | } else { |
||
88 | $response = new JsonResponse(['status' => 'error', 'message' => 'wrong input']); |
||
89 | } |
||
90 | 3 | return $response; |
|
91 | } |
||
92 | 6 | $params = $paramsProcessor->getValues(); |
|
93 | |||
94 | try { |
||
95 | 6 | $response = $handler->handle($params); |
|
96 | 6 | $outputValid = count($handler->outputs()) === 0; // back compatibility for handlers with no outputs defined |
|
97 | 6 | $outputValidatorErrors = []; |
|
98 | 6 | foreach ($handler->outputs() as $output) { |
|
99 | 3 | $validationResult = $output->validate($response); |
|
100 | 3 | if ($validationResult->isOk()) { |
|
101 | 3 | $outputValid = true; |
|
102 | 3 | break; |
|
103 | } |
||
104 | $outputValidatorErrors[] = $validationResult->getErrors(); |
||
105 | } |
||
106 | 6 | if (!$outputValid) { |
|
107 | $response = new JsonApiResponse(Response::S500_INTERNAL_SERVER_ERROR, ['status' => 'error', 'message' => 'Internal server error', 'details' => $outputValidatorErrors]); |
||
108 | } |
||
109 | 6 | $code = $response->getCode(); |
|
110 | } catch (Throwable $exception) { |
||
111 | if (!Debugger::$productionMode) { |
||
112 | $response = new JsonApiResponse(Response::S500_INTERNAL_SERVER_ERROR, ['status' => 'error', 'message' => 'Internal server error', 'detail' => $exception->getMessage()]); |
||
113 | } else { |
||
114 | $response = new JsonApiResponse(Response::S500_INTERNAL_SERVER_ERROR, ['status' => 'error', 'message' => 'Internal server error']); |
||
115 | } |
||
116 | $code = $response->getCode(); |
||
117 | Debugger::log($exception, Debugger::EXCEPTION); |
||
118 | } |
||
119 | |||
120 | 6 | $end = microtime(true); |
|
121 | |||
122 | 6 | if ($this->context->findByType(ApiLoggerInterface::class)) { |
|
123 | /** @var ApiLoggerInterface $apiLogger */ |
||
124 | $apiLogger = $this->context->getByType(ApiLoggerInterface::class); |
||
125 | $this->logRequest($request, $apiLogger, $code, $end - $start); |
||
126 | } |
||
127 | |||
128 | // output to nette |
||
129 | 6 | $this->response->setCode($code); |
|
130 | 6 | return $response; |
|
131 | } |
||
132 | |||
133 | 12 | private function getApi(Request $request): Api |
|
142 | |||
143 | 12 | private function checkAuth(ApiAuthorizationInterface $authorization): ?IResponse |
|
151 | |||
152 | 9 | private function checkRateLimit(RateLimitInterface $rateLimit): ?IResponse |
|
173 | |||
174 | private function logRequest(Request $request, ApiLoggerInterface $logger, int $code, float $elapsed): void |
||
204 | |||
205 | 12 | protected function sendCorsHeaders(): void |
|
227 | |||
228 | private function getRequestDomain(): ?string |
||
243 | } |
||
244 |