Auf dem Testserver lief die App beim Scrollen langer Timelines in ein 429: nach jeder Scroll-Pause ging sofort ein PUT /settings raus, und jedes sichtbare Vorschaubild zählte auf dasselbe Limit von 300/min. Danach blieben Bilder leer und "Einstellungen konnten nicht gespeichert werden" erschien. Backend: Medien-Reads bekommen einen eigenen Limiter (media, 1200/min), POST /client-logs nimmt Warnungen und Fehler der App an (client-logs, 30/min) und schreibt sie in den täglichen Kanal `client`; Exceptions tragen user_id, path und method im Kontext. Frontend: Einstellungen gehen frühestens 1,5 s nach der letzten Änderung und nur bei geändertem Inhalt zum Server, 429/Netzfehler werden still mit Backoff wiederholt, nur eine Ablehnung wird gemeldet. Bilder laufen durch ein Gate (6 parallel, Retry-After, 3 Versuche) und fallen auf einen Platzhalter statt auf einen kaputten Pfad zurück. Neuer clientLog-Service mit IndexedDB-Puffer, Dedupe und Batch-Versand, globaler Fehlerfang im Boot, Protokoll im Entwickler-Tab. Drei pulsierende Punkte statt weißer Fläche in Timeline-Punkt, Key Image und Medienkacheln. Dokumentation in documentation/logging.md. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
55 lines
1.7 KiB
PHP
55 lines
1.7 KiB
PHP
<?php
|
|
|
|
namespace App\Providers;
|
|
|
|
use Illuminate\Cache\RateLimiting\Limit;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\RateLimiter;
|
|
use Illuminate\Support\ServiceProvider;
|
|
|
|
class AppServiceProvider extends ServiceProvider
|
|
{
|
|
/**
|
|
* Register any application services.
|
|
*/
|
|
public function register(): void
|
|
{
|
|
//
|
|
}
|
|
|
|
/**
|
|
* Bootstrap any application services.
|
|
*/
|
|
public function boot(): void
|
|
{
|
|
$this->configureRateLimiting();
|
|
}
|
|
|
|
/**
|
|
* Rate limiters for the API surface consumed by the Quasar app.
|
|
*/
|
|
private function configureRateLimiting(): void
|
|
{
|
|
RateLimiter::for('api', function (Request $request): Limit {
|
|
return $request->user()
|
|
? Limit::perMinute(300)->by('api|user|'.$request->user()->getAuthIdentifier())
|
|
: Limit::perMinute(60)->by('api|ip|'.$request->ip());
|
|
});
|
|
|
|
RateLimiter::for('uploads', function (Request $request): Limit {
|
|
return Limit::perMinute(30)->by('uploads|user|'.$request->user()?->getAuthIdentifier());
|
|
});
|
|
|
|
// Thumbnails, previews and originals. One request per image the
|
|
// timeline shows, so this has to be far above the write budget.
|
|
RateLimiter::for('media', function (Request $request): Limit {
|
|
return Limit::perMinute(1200)->by('media|user|'.$request->user()?->getAuthIdentifier());
|
|
});
|
|
|
|
// Client-side log batches; the app sends at most one every few
|
|
// seconds, so anything beyond this is a misbehaving client.
|
|
RateLimiter::for('client-logs', function (Request $request): Limit {
|
|
return Limit::perMinute(30)->by('client-logs|user|'.$request->user()?->getAuthIdentifier());
|
|
});
|
|
}
|
|
}
|