fn_b/app/Http/Middleware/EditorialAuthentication.php

44 lines
2.2 KiB
PHP

<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
final class EditorialAuthentication
{
public function handle(Request $request, Closure $next)
{
abort_unless(config('editorial.enabled'), 503, 'Editorial API disabled');
if (app()->environment('local')) {
$db = config('database.connections.'.config('database.default'));
abort_unless(($db['database'] ?? '') === 'fna_v3' && in_array($db['host'] ?? '', ['localhost', '127.0.0.1']) && empty($db['url']), 503, 'Local editorial database binding mismatch');
}
$path = config('editorial.credential_file');
$credential = is_file($path) ? json_decode(file_get_contents($path), true) : null;
abort_unless(is_array($credential)
&& ($credential['environment'] ?? null) === app()->environment()
&& ($credential['project'] ?? null) === 'fna-v3'
&& ($credential['schema'] ?? null) === 'gemdocs.credential.v1'
&& ($credential['base_url'] ?? null) === config('editorial.base_url')
&& preg_match('/\Afna_ed_[a-f0-9]{64}\z/', $credential['fields']['api_key'] ?? ''), 503, 'Editorial credential unavailable');
abort_unless(hash_equals($credential['fields']['api_key'], $request->bearerToken() ?? ''), 401, 'Invalid editorial credential');
try {
$response = $next($request);
} catch (\Illuminate\Database\QueryException $e) {
return response()->json(['message' => 'Concurrent or incompatible database write; query current status before retry'], 409);
} catch (\Throwable $e) {
if ($e instanceof \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface || $e instanceof \Illuminate\Validation\ValidationException) {
throw $e;
}
\Illuminate\Support\Facades\Log::error('Editorial operation failed', ['exception_type' => get_class($e)]);
return response()->json(['message' => 'Editorial operation failed; query the request ID before retrying'], 500);
}
$response->headers->set('Cache-Control', 'private, no-store');
$response->headers->set('X-Robots-Tag', 'noindex, nofollow');
return $response;
}
}