configureRateLimiting(); $this->routes(function () { // Tiefer Health-Check für den externen Monitor: auf jeder Domain, // ohne web-Gruppe (keine Session, kein CSRF, kein SubdomainResolver) Route::get('/health', HealthController::class) ->middleware('throttle:30,1') ->name('health.deep'); // API-Routen werden global geladen Route::domain('api.'.config('app.domain').config('app.tld_care')) ->middleware('api') ->namespace($this->namespace) ->group(base_path('routes/api.php')); // Web-Routen werden domain-bewusst geladen Route::middleware('web') ->namespace($this->namespace) ->group(function () { $this->loadDomainAwareRoutes(); }); }); } /** * Lädt Routen basierend auf der domains.php Konfiguration. * Vereinfachter Ansatz ohne DomainContext-Abhängigkeit */ protected function loadDomainAwareRoutes(): void { $request = $this->app->make('request'); $this->loadSharedRoutes(); // Lade alle Domain-spezifischen Routen // Die Domain-Logik wird von SubdomainResolver Middleware behandelt $domainType = EarlyDomainParser::getCurrentDomainType($request->getHost()); $routesToLoad = match ($domainType) { 'main' => ['main'], 'main-shop' => ['shop', 'portal'], 'user-shop' => ['user-shop', 'portal'], 'crm' => ['crm'], 'portal' => ['portal'], 'checkout' => ['checkout'], 'unknown' => ['main'], // Fallback für unbekannte Domains default => ['main'], }; foreach ($routesToLoad as $routeType) { $this->loadDomainRoutes($routeType, $routeType.'.php'); } if (config('app.debug')) { \Log::channel('domain')->info('Domain-aware routes loaded', [ 'domain_type' => $domainType, 'routes_loaded' => $routesToLoad, 'host' => EarlyDomainParser::parseDomain()['host'], ]); } } /** * Lädt eine spezifische Routendatei für eine Domain. */ protected function loadDomainRoutes(string $domainType, string $fileName): void { $routePath = base_path('routes/domains/'.$fileName); if (file_exists($routePath)) { Route::group([], $routePath); } else { if (config('app.debug')) { \Log::channel('domain')->warning('Domain route file not found', [ 'domain_type' => $domainType, 'file_path' => $routePath, ]); } } } /** * Lädt Routen, die auf allen Domains verfügbar sein sollen. */ protected function loadSharedRoutes(): void { // Lädt Routen, die auf allen Domains verfügbar sein sollen. Route::group([], base_path('routes/shared/common.php')); } /** * Konfiguriert die Rate-Limiter für die Anwendung. * * @return void */ protected function configureRateLimiting() { RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()); }); RateLimiter::for('contact-form', function (Request $request) { $tooManyRequests = $this->formTooManyRequestsResponse('contact'); return [ Limit::perMinutes(10, 5)->by('contact-ip:'.$request->ip())->response($tooManyRequests), Limit::perDay(5)->by('contact-email:'.mb_strtolower((string) $request->input('email')))->response($tooManyRequests), ]; }); RateLimiter::for('register-form', function (Request $request) { $tooManyRequests = $this->formTooManyRequestsResponse('register'); return [ Limit::perMinutes(10, 5)->by('register-ip:'.$request->ip())->response($tooManyRequests), Limit::perDay(20)->by('register-ip-day:'.$request->ip())->response($tooManyRequests), ]; }); } /** * Zurück zum Formular mit Hinweis statt nackter 429-Seite. * * @param 'contact'|'register' $form */ private function formTooManyRequestsResponse(string $form): Closure { return function (Request $request) use ($form) { FormSpamGuard::log($request, $form, 'rate_limit'); return back()->withInput($request->except('password', 'password_confirmation')) ->withErrors(['email' => __('form_protection.too_many_requests')]); }; } }