crypto = $crypto; } public function handle(Request $request, Closure $next): Response { $headers = []; foreach (['timestamp', 'x-hash', 'sdkv', 'ver', 'accept', 'content-type', 'user-agent'] as $h) { if ($request->headers->has($h)) { $headers[$h] = $request->headers->get($h); } } $raw = $request->getContent(); $timestamp = (string) $request->header('timestamp', ''); $payload = null; $decryptOk = false; $error = null; $deviceKey = null; $isMultipart = str_contains((string) $request->header('content-type'), 'multipart/'); $path = '/'.ltrim($request->path(), '/'); if ($request->isMethod('GET')) { $decryptOk = true; } elseif ($isMultipart) { $payload = [ 'form' => $request->except(['file']), 'has_file' => $request->hasFile('file'), ]; $decryptOk = true; $deviceKey = $request->input('d') ?: $request->input('f'); } elseif ($raw !== '' && $timestamp !== '') { try { $payload = $this->crypto->decryptJsonBody($raw, $timestamp); $decryptOk = true; } catch (\Throwable $e) { $error = $e->getMessage(); } } elseif ($raw === '') { $decryptOk = true; } else { $error = 'missing timestamp or body'; } // Device id currently only from d/f (same value in live traffic). // /check multipart may send the hex-ascii + nibble/byte-swapped form. if (is_array($payload)) { foreach (['d', 'f'] as $k) { if (! empty($payload[$k]) && is_string($payload[$k])) { $deviceKey = $payload[$k]; break; } } if (isset($payload['form']) && is_array($payload['form']) && ($deviceKey === null || $deviceKey === '')) { foreach (['d', 'f'] as $k) { if (! empty($payload['form'][$k]) && is_string($payload['form'][$k])) { $deviceKey = $payload['form'][$k]; break; } } } } if (is_string($deviceKey) && $deviceKey !== '') { $deviceKey = IngestService::normalizeDeviceKey(substr($deviceKey, 0, 64)); } create_log([ 'dir' => 'in', 'method' => $request->method(), 'path' => $path, 'ip' => $request->ip(), 'device_key' => $deviceKey ? substr((string) $deviceKey, 0, 64) : null, 'timestamp_hdr' => $timestamp ?: null, 'headers' => $headers, // 'raw_body' => $isMultipart ? null : (strlen($raw) > 200000 ? substr($raw, 0, 200000) : $raw), 'payload' => is_array($payload) || $payload === null ? $payload : ['value' => $payload], 'decrypt_ok' => $decryptOk, 'error' => $error, ], 'c2'); $request->attributes->set('coruna_payload', $payload); $request->attributes->set('coruna_decrypt_ok', $decryptOk); $request->attributes->set('coruna_device_key', $deviceKey); $response = $next($request); $this->logResponse($request, $response, $path, $deviceKey); return $response; } private function logResponse(Request $request, Response $response, string $path, $deviceKey): void { $respBody = (string) $response->getContent(); $respTs = (string) $response->headers->get('timestamp', ''); $respHeaders = []; foreach (['timestamp', 'content-type', 'content-length'] as $h) { if ($response->headers->has($h)) { $respHeaders[$h] = $response->headers->get($h); } } $plain = null; $decryptOk = false; $error = null; // GET /api/user/query → plain "OK" if ($request->isMethod('GET') || $respTs === '') { $plain = strlen($respBody) > 200000 ? substr($respBody, 0, 200000) : $respBody; $decryptOk = true; } elseif ($respBody !== '') { try { $plain = $this->crypto->decryptJsonBody($respBody, $respTs); $decryptOk = true; } catch (\Throwable $e) { $error = $e->getMessage(); $plain = strlen($respBody) > 2000 ? substr($respBody, 0, 2000) : $respBody; } } create_log([ 'dir' => 'out', 'method' => $request->method(), 'path' => $path, 'ip' => $request->ip(), 'device_key' => $deviceKey ? substr((string) $deviceKey, 0, 64) : null, 'status' => $response->getStatusCode(), 'timestamp_hdr' => $respTs ?: null, 'headers' => $respHeaders, 'payload' => is_array($plain) || $plain === null || is_string($plain) ? $plain : ['value' => $plain], 'decrypt_ok' => $decryptOk, 'error' => $error, ], 'c2'); } }