payload($request); } catch (JsonException $exception) { return $this->jsonResponse([ 'code' => 400, 'message' => 'Invalid JSON body.', 'errors' => ['body' => $exception->getMessage()], ], 400); } $service = new ArticleImportService(); $result = $service->import($payload); if (!$result['ok']) { return $this->jsonResponse([ 'code' => 422, 'message' => 'Archive import validation failed.', 'errors' => $result['errors'], ], 422); } try { $service->persistSnapshot($result['data']); (new \app\service\ArchiveRepository())->saveImport($result['data']); if (($result['data']['queue']['needs_ai_metadata'] ?? false) === true) { (new \app\service\AiMetadataQueue())->push($result['data']['archive']['archive_uid']); $result['data']['queue']['ai_metadata_enqueued'] = true; } } catch (Throwable $exception) { return $this->jsonResponse([ 'code' => 500, 'message' => 'Archive import data could not be saved.', 'errors' => ['storage' => $exception->getMessage()], ], 500); } return $this->jsonResponse([ 'code' => 0, 'message' => 'Archive imported.', 'data' => $result['data'], ], 201); } /** * @throws JsonException */ private function payload(Request $request): array { $payload = array_merge($request->get(), $request->post()); $file = $request->file('file'); if ($file instanceof UploadFile) { if (!$file->isValid()) { return $payload + ['file_error' => $file->getUploadErrorCode()]; } $payload['content'] = file_get_contents($file->getPathname()) ?: ''; $payload['source'] = $payload['source'] ?? $file->getUploadName(); $payload['title'] = $payload['title'] ?? pathinfo($file->getUploadName() ?? '', PATHINFO_FILENAME); $payload['metadata'] = $this->metadata($payload['metadata'] ?? null); return $payload; } $rawBody = trim($request->rawBody()); if ($rawBody === '') { $payload['metadata'] = $this->metadata($payload['metadata'] ?? null); return $payload; } if ($this->isJsonRequest($request)) { $jsonPayload = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR); if (!is_array($jsonPayload)) { return []; } $jsonPayload['metadata'] = $this->metadata($jsonPayload['metadata'] ?? null); return $jsonPayload; } $payload['content'] = $rawBody; $payload['source'] = $payload['source'] ?? 'raw-markdown'; $payload['metadata'] = $this->metadata($payload['metadata'] ?? null); return $payload; } private function isJsonRequest(Request $request): bool { $contentType = strtolower($request->header('content-type', '')); return str_contains($contentType, 'application/json') || str_contains($contentType, '+json'); } private function metadata(mixed $metadata): array { if (is_array($metadata)) { return $metadata; } if (!is_string($metadata) || trim($metadata) === '') { return []; } try { $decoded = json_decode($metadata, true, 512, JSON_THROW_ON_ERROR); return is_array($decoded) ? $decoded : []; } catch (JsonException) { return []; } } private function jsonResponse(array $data, int $status): Response { return response( json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR), $status, ['Content-Type' => 'application/json'] ); } }