- LogApiUsage als terminable Middleware: der ApiUsageLog-INSERT laeuft nach gesendeter Antwort und verlaengert nicht mehr die Request-Dauer. Startzeit wird ueber ein Request-Attribut uebergeben (terminate() laeuft auf frischer Instanz). - User::openSinglePurchaseCount() memoisiert die offenen Einmalkaeufe pro Request. Vorher wurde derselbe grantingSubmission-Count im Status-Widget mehrfach (Quota-Total, Quota-Remaining, openSinglePurchases) plus ein exists() in hasActiveBooking abgesetzt -> jetzt eine Query. - CustomerCompanyContext::selectedCompany() nutzt eine einzige first()-Query statt exists()+first(); das Verwerfen nicht (mehr) erreichbarer Auswahlen bleibt erhalten. - LegacySubscriptionService::cancelBundleFor() ist ein einziges Bulk-Update statt Update-pro-Iteration (UserPaymentOption hat keine Observer/Events). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
60 lines
2 KiB
PHP
60 lines
2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Middleware;
|
|
|
|
use App\Models\ApiUsageLog;
|
|
use Closure;
|
|
use Illuminate\Http\Request;
|
|
use Laravel\Sanctum\PersonalAccessToken;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class LogApiUsage
|
|
{
|
|
private const StartedAtKey = 'api_usage_started_at';
|
|
|
|
/**
|
|
* Handle an incoming request.
|
|
*
|
|
* @param Closure(Request): (Response) $next
|
|
*/
|
|
public function handle(Request $request, Closure $next): Response
|
|
{
|
|
$request->attributes->set(self::StartedAtKey, microtime(true));
|
|
|
|
return $next($request);
|
|
}
|
|
|
|
/**
|
|
* Der INSERT läuft erst nach gesendeter Antwort (terminable Middleware) und
|
|
* verlängert damit nicht mehr die wahrgenommene Request-Dauer. Der Startwert
|
|
* wird über die Request-Attribute übergeben, da `terminate()` auf einer
|
|
* frischen Middleware-Instanz aufgerufen wird.
|
|
*/
|
|
public function terminate(Request $request, Response $response): void
|
|
{
|
|
$startedAt = (float) $request->attributes->get(self::StartedAtKey, microtime(true));
|
|
|
|
$this->writeLog($request, $response, $startedAt);
|
|
}
|
|
|
|
private function writeLog(Request $request, Response $response, float $startedAt): void
|
|
{
|
|
$token = $request->user()?->currentAccessToken();
|
|
$tokenId = $token instanceof PersonalAccessToken && (int) $token->getKey() > 0
|
|
? (int) $token->getKey()
|
|
: null;
|
|
|
|
ApiUsageLog::query()->create([
|
|
'user_id' => $request->user()?->id,
|
|
'personal_access_token_id' => $tokenId,
|
|
'method' => $request->method(),
|
|
'path' => '/'.$request->path(),
|
|
'route_name' => $request->route()?->getName(),
|
|
'status_code' => $response->getStatusCode(),
|
|
'ip_address' => $request->ip(),
|
|
'user_agent' => mb_substr((string) $request->userAgent(), 0, 512),
|
|
'duration_ms' => (int) round((microtime(true) - $startedAt) * 1000),
|
|
'requested_at' => now(),
|
|
]);
|
|
}
|
|
}
|