proofdb/app/controller/Api/ArticleImportController.php
2026-05-01 23:40:14 +08:00

134 lines
4.3 KiB
PHP

<?php
namespace app\controller\Api;
use app\service\ArticleImportService;
use JsonException;
use support\Request;
use support\Response;
use Webman\Http\UploadFile;
use Throwable;
class ArticleImportController
{
public function import(Request $request): Response
{
try {
$payload = $this->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']
);
}
}