67 lines
3.1 KiB
PHP
67 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Editorial;
|
|
|
|
use Illuminate\Contracts\Http\Kernel;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\UploadedFile;
|
|
|
|
/** Calls the existing HTTP routes on the application server, without opening its public API. */
|
|
final class ProcessApi
|
|
{
|
|
private array $credential;
|
|
|
|
public function __construct()
|
|
{
|
|
abort_unless(PHP_SAPI === 'cli', 403);
|
|
$this->credential = ['schema' => 'gemdocs.credential.v1', 'project' => 'fna-v3',
|
|
'environment' => app()->environment(), 'base_url' => config('editorial.base_url'),
|
|
'expires_at' => time() + 600, 'fields' => ['api_key' => 'fna_ed_'.bin2hex(random_bytes(32))]];
|
|
}
|
|
|
|
public function call(string $method, string $path, array $data = [], ?UploadedFile $image = null): array
|
|
{
|
|
abort_unless(in_array($method, ['GET', 'POST']) && preg_match('~\A[a-zA-Z0-9_/-]+\z~', $path), 422);
|
|
$response = $this->dispatch($method, '/api/editorial/v1/'.$path, $data, $image);
|
|
$body = json_decode($response->getContent(), true);
|
|
abort_unless($response->isSuccessful(), $response->getStatusCode(), 'Editorial API rejected operation; inspect state before retry');
|
|
abort_unless(is_array($body), 502, 'Unexpected API response');
|
|
|
|
return $body;
|
|
}
|
|
|
|
public function preview(string $url): string
|
|
{
|
|
$base = rtrim(config('editorial.base_url'), '/');
|
|
abort_unless(str_starts_with($url, $base.'/api/editorial/v1/preview/'), 422, 'Preview origin mismatch');
|
|
$response = $this->dispatch('GET', substr($url, strlen($base)));
|
|
abort_unless($response->isSuccessful() && str_contains($response->headers->get('Content-Type', ''), 'text/html')
|
|
&& str_contains($response->headers->get('X-Robots-Tag', ''), 'noindex'), 502, 'Preview render failed');
|
|
|
|
return $response->getContent();
|
|
}
|
|
|
|
private function dispatch(string $method, string $path, array $data = [], ?UploadedFile $image = null)
|
|
{
|
|
abort_unless(time() < $this->credential['expires_at'], 401, 'Process session expired');
|
|
$before = ['editorial.enabled' => config('editorial.enabled'),
|
|
'editorial.process_credential' => config('editorial.process_credential')];
|
|
$originalRequest = app('request');
|
|
config(['editorial.enabled' => true, 'editorial.process_credential' => $this->credential]);
|
|
try {
|
|
$server = ['HTTP_ACCEPT' => 'application/json', 'HTTP_AUTHORIZATION' => 'Bearer '.$this->credential['fields']['api_key'],
|
|
'CONTENT_TYPE' => 'application/json', 'REMOTE_ADDR' => '127.0.0.1'];
|
|
$request = Request::create(rtrim(config('editorial.base_url'), '/').$path, $method, [], [],
|
|
$image ? ['image' => $image] : [], $server, $image ? null : json_encode($data));
|
|
$kernel = app(Kernel::class);
|
|
$response = $kernel->handle($request);
|
|
$kernel->terminate($request, $response);
|
|
|
|
return $response;
|
|
} finally {
|
|
config($before);
|
|
app()->instance('request', $originalRequest);
|
|
}
|
|
}
|
|
}
|