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>
47 lines
2.4 KiB
PHP
47 lines
2.4 KiB
PHP
<?php
|
|
|
|
use App\Http\Controllers\Api\AuthController;
|
|
use App\Http\Controllers\Api\ClientLogController;
|
|
use App\Http\Controllers\Api\EventController;
|
|
use App\Http\Controllers\Api\EventMediaController;
|
|
use App\Http\Controllers\Api\SettingsController;
|
|
use App\Http\Controllers\Api\SettingsMediaController;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Route;
|
|
|
|
Route::post('/login', [AuthController::class, 'login'])->middleware('throttle:api');
|
|
|
|
Route::middleware(['auth:api', 'throttle:api'])->group(function () {
|
|
Route::get('/user', fn (Request $request) => $request->user());
|
|
Route::post('/logout', [AuthController::class, 'logout']);
|
|
Route::get('/settings', [SettingsController::class, 'show']);
|
|
Route::put('/settings', [SettingsController::class, 'update']);
|
|
Route::post('/settings/media/background', [SettingsMediaController::class, 'store'])
|
|
->middleware('throttle:uploads');
|
|
Route::delete('/settings/media/background', [SettingsMediaController::class, 'destroy']);
|
|
Route::get('/events/{clientId}/media', [EventMediaController::class, 'index']);
|
|
Route::post('/events/{clientId}/media', [EventMediaController::class, 'store'])
|
|
->middleware('throttle:uploads');
|
|
Route::patch('/events/{clientId}/media/{media}', [EventMediaController::class, 'update']);
|
|
Route::delete('/events/{clientId}/media/{media}', [EventMediaController::class, 'destroy']);
|
|
|
|
Route::apiResource('events', EventController::class)->parameters([
|
|
'events' => 'clientId',
|
|
]);
|
|
Route::post('/events/sync', [EventController::class, 'sync']);
|
|
});
|
|
|
|
// Browser-side warnings and errors, batched by the app. Own budget so a
|
|
// client that is failing loudly cannot lock itself out of the real API.
|
|
Route::middleware(['auth:api', 'throttle:client-logs'])->group(function () {
|
|
Route::post('/client-logs', [ClientLogController::class, 'store']);
|
|
});
|
|
|
|
// Image reads get their own budget: a long timeline fetches one thumbnail per
|
|
// visible event, and that must never eat into the limit for the actual API
|
|
// writes (which is what showed up as "Einstellungen konnten nicht gespeichert
|
|
// werden" while scrolling).
|
|
Route::middleware(['auth:api', 'throttle:media'])->group(function () {
|
|
Route::get('/settings/media/background', [SettingsMediaController::class, 'show']);
|
|
Route::get('/event-media/{media}/{variant}', [EventMediaController::class, 'show']);
|
|
});
|