app->bind(ICache::class, CacheLti::class); $this->app->bind(ICookie::class, CookieLti::class); $this->app->bind(IDatabase::class, DatabaseLti::class); $this->app->bind(ILtiServiceConnector::class, fn ($app) => (new LtiServiceConnector($app->make(ICache::class), new Client)) ->setDebuggingMode(config('app.debug'))); } /** * Bootstrap any application services. */ public function boot(): void { // Tolérance d'horloge pour les jetons signés par Moodle (LTI 1.3). JWT::$leeway = 5; // Coquille de l'application (layouts/app) : variables communes à // la barre du haut et à la barre latérale, pour qu'aucun contrôleur // n'ait à les repasser (séparation vitrine/application, 2026-10-13). View::composer('layouts.app', function ($vue): void { $prof = Auth::guard('web')->user(); $compte = $prof ?? Auth::guard('eleve')->user(); $vue->with([ 'compte' => $compte, 'estProf' => $compte instanceof User, 'nomAffiche' => $compte instanceof Eleve ? $compte->nomAffiche() : $compte->name, 'raccourcis' => $compte->raccourcis, 'badgeMesDevoirs' => $compte instanceof Eleve ? CompteurDevoirsEnAttente::pour($compte) : 0, ]); }); // API publique (bloc B) : quota par jeton Sanctum, pas par compte ni // par IP — un même prof avec deux jetons a deux quotas séparés. // CACHE_STORE=redis en production (voir CLAUDE.md : « Redis pour les // quotas »), stocké dans config/cache.php -> 'database' en local/test. RateLimiter::for('api', fn (Request $request) => Limit::perMinute(60)->by( $request->user()?->currentAccessToken()?->id ?? $request->ip() )); } }