mivita/dev/routes-optimization/DomainResolverBak.php
2025-10-20 17:42:08 +02:00

284 lines
13 KiB
PHP

<?php
namespace App\Http\Middleware;
use Closure;
use App\Services\Util;
use App\Models\UserShop;
use Illuminate\Http\Request;
use App\Domain\DomainContext;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Session;
class DomainResolver
{
/**
* Behandelt eine eingehende Anfrage, um den Domain-Kontext aufzulösen.
*
* Diese Middleware ist schlank gehalten. Die Hauptlogik zur Erstellung
* des DomainContext befindet sich im DomainServiceProvider, um eine
* saubere Trennung der Verantwortlichkeiten zu gewährleisten.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
// Nur für relevante Requests (keine API, Assets, etc.)
if (!$this->shouldHandleRequest($request)) {
return $next($request);
}
/** @var DomainContext $context */
$context = app(DomainContext::class);
// Session-Domain für alle Subdomains setzen (wichtig für Cookie-Sharing)
// Hinweis: DomainResolver läuft jetzt VOR StartSession - ideale Position!
// Für User-Shops: Immer die korrekte Domain setzen
if ($context->isUserShop()) {
$userShopDomain = '.' . parse_url($context->host, PHP_URL_HOST);
Config::set('session.domain', $userShopDomain);
if (config('app.debug')) {
\Log::channel('domain')->debug('DomainResolver: User-Shop Session-Domain VOR StartSession gesetzt', [
'user_shop_domain' => $userShopDomain,
'context_host' => $context->host,
'session_started' => Session::isStarted(),
'request_host' => $request->getHost(),
'solution' => 'Korrekte Kernel-Reihenfolge: DomainResolver → EncryptCookies → AddQueuedCookiesToResponse → StartSession'
]);
}
} else {
// Für Main-Domain: Standard-Domain verwenden
$mainDomain = '.' . config('app.domain') . config('app.tld_care');
Config::set('session.domain', $mainDomain);
if (config('app.debug')) {
\Log::channel('domain')->debug('DomainResolver: Main-Domain Session-Domain VOR StartSession gesetzt', [
'main_domain' => $mainDomain,
'session_started' => Session::isStarted(),
'request_host' => $request->getHost(),
'solution' => 'Korrekte Kernel-Reihenfolge: DomainResolver → EncryptCookies → AddQueuedCookiesToResponse → StartSession'
]);
}
}
// Wenn der DomainServiceProvider die Domain nicht identifizieren konnte,
// leiten wir sicher auf die Hauptdomain um.
if ($context->isUnknown()) {
// Detailliertes Logging für spätere Analyse
if (config('app.debug')) {
\Log::channel('domain')->warning('Unknown domain accessed', [
'host' => $request->getHost(),
'subdomain' => $context->subdomain,
'user_agent' => $request->userAgent(),
'ip' => $request->ip(),
'referer' => $request->header('referer'),
'path' => $request->getPathInfo(),
'session_id_at_unknown_domain' => Session::isStarted() ? Session::getId() : 'session_not_started'
]);
}
// Holt die URL der Hauptdomain vom DomainService und leitet um.
$mainUrl = app(\App\Services\DomainService::class)->buildUrl('main');
return redirect()->away($mainUrl, 301);
}
if (config('app.debug')) {
\Log::channel('domain')->debug('DomainResolver: context', [
'context' => $context,
'subdomain' => $context->subdomain
]);
}
// Für User-Shop-Domains: Validierung und Route-Parameter-Bereinigung
if ($context->isUserShop()) {
// Validiere UserShop-Berechtigung (bereits im DomainServiceProvider geprüft,
// aber zusätzliche Sicherheitsebene)
if (!$context->userShop) {
if (config('app.debug')) {
\Log::channel('domain')->warning('UserShop not found', [
'subdomain' => $context->subdomain,
'host' => $context->host
]);
}
abort(503, 'Shop not available');
}
if (!$context->userShop->active) {
if (config('app.debug')) {
\Log::channel('domain')->info('UserShop inactive accessed', [
'shop_id' => $context->userShop->id,
'subdomain' => $context->subdomain
]);
}
abort(503, 'Shop temporarily unavailable');
}
if (!$context->userShop->user || !$context->userShop->user->isActiveShop()) {
if (config('app.debug')) {
\Log::channel('debug')->info('UserShop with expired payment accessed', [
'shop_id' => $context->userShop->id,
'user_id' => $context->userShop->user_id ?? null,
'subdomain' => $context->subdomain
]);
}
abort(503, 'Shop access denied');
}
// Route-Parameter-Bereinigung passiert in setupLegacyContext
}
// Richtet den Anwendungskontext für Abwärtskompatibilität ein.
$this->setupLegacyContext($context, $request);
// Session-ID in Request-Attributes speichern für Vergleich in anderen Middleware
$currentSessionId = Session::isStarted() ? Session::getId() : null;
$request->attributes->set('domain_resolver_session_id', $currentSessionId);
// Debug-Logging für Session-Konsistenz vor dem nächsten Request
if (config('app.debug')) {
\Log::channel('domain')->debug('DomainResolver: Middleware beendet - Session-Status VOR next()', [
'current_session_id' => $currentSessionId,
'session_started' => Session::isStarted(),
'user_shop_id' => session('user_shop')?->id,
'user_shop_domain' => session('user_shop_domain'),
'context_type' => $context->type,
'session_domain_config' => config('session.domain'),
'request_host' => $request->getHost(),
'session_all_keys' => Session::isStarted() ? array_keys(Session::all()) : [],
'middleware_order' => 'DomainResolver → EncryptCookies → AddQueuedCookiesToResponse → StartSession → ...',
'domain_fix_applied' => $context->isUserShop() ? 'User-Shop Domain VOR StartSession gesetzt' : 'Main-Domain VOR StartSession gesetzt'
]);
}
return $next($request);
}
/**
* Stellt die Kompatibilität mit älteren Teilen der Anwendung her,
* die direkt auf Session-Daten oder dynamische Konfigurationen zugreifen.
*
* @param DomainContext $context
* @param Request $request
*/
private function setupLegacyContext(DomainContext $context, Request $request): void
{
// TODO: [TECH-DEBT] Diese Methode sollte langfristig entfernt werden.
// Alle Teile der Anwendung sollten den DomainContext direkt verwenden.
if (config('app.debug')) {
\Log::channel('domain')->debug('DomainResolver: setupLegacyContext - Session ist jetzt verfügbar', [
'session_user_shop_id' => session('user_shop')?->id,
'session_user_shop_name' => session('user_shop')?->name,
'context_user_shop_id' => $context->userShop?->id,
'context_user_shop_name' => $context->userShop?->name,
'context_type' => $context->type,
'session_id' => Session::getId(),
'session_started' => Session::isStarted(),
'session_domain' => config('session.domain'),
'request_host' => request()->getHost()
]);
}
if ($context->userShop) {
// Setzt die alten Session-Variablen, die von einigen Views/Controllern erwartet werden.
Session::put('user_shop', $context->userShop);
Session::put('user_shop_domain', $context->host);
// Session sofort speichern um Konsistenz sicherzustellen
Session::save();
// Route-Parameter-Bereinigung passiert in separater Middleware
// nachdem die Route vollständig aufgelöst wurde
if (config('app.debug')) {
\Log::channel('domain')->debug('DomainResolver: user_shop gesetzt', [
'user_id' => $context->userShop->user_id ?? null,
'context_user_shop_id' => $context->userShop->id,
'session_user_shop_id' => session('user_shop')?->id,
'session_user_shop_name' => session('user_shop')?->name,
'session_saved' => true,
'session_id' => Session::getId(),
'session_domain' => config('session.domain'),
'request_host' => request()->getHost(),
'route_cleanup_delegated' => true
]);
}
// Setzt die app.url zur Laufzeit, um URL-Generierung in alten Teilen zu ermöglichen.
Config::set('app.url', $context->host);
// Kompatibilität mit der Util-Klasse.
Util::setPostRoute('user/');
} else {
if ($context->type === 'main') {
Session::forget('user_shop');
Session::forget('user_shop_domain');
// Session sofort speichern um Konsistenz sicherzustellen
Session::save();
if (config('app.debug')) {
\Log::channel('domain')->debug('DomainResolver: user_shop entfernt (' . $context->type . ' domain)', ['user_shop' => session('user_shop')]);
}
Config::set('app.url', $context->host);
} elseif ($context->type === 'shop') {
Util::setPostRoute('user/');
$user_shop = UserShop::where('slug', 'aloevera')->first();
Session::put('user_shop', $user_shop);
Session::put('user_shop_domain', $context->host);
// Session sofort speichern um Konsistenz sicherzustellen
Session::save();
if (config('app.debug')) {
\Log::channel('domain')->debug('DomainResolver: user_shop hinzugefügt (' . $context->type . ' domain)', ['user_shop' => session('user_shop')]);
}
Config::set('app.url', $context->host);
} else {
// Für Domains ohne UserShop: Session-Daten sofort löschen
Session::forget('user_shop');
Session::put('user_shop_domain', $context->host);
// Session sofort speichern um Konsistenz sicherzustellen
Session::save();
\Log::debug('DomainResolver: user_shop_domain hinzugefügt (' . $context->type . ' domain)', ['user_shop' => session('user_shop')]);
Config::set('app.url', $context->host);
}
}
// Abschließendes Debug-Logging für Session-Konsistenz
if (config('app.debug')) {
\Log::channel('domain')->debug('DomainResolver: setupLegacyContext abgeschlossen', [
'final_session_id' => Session::getId(),
'user_shop_id' => session('user_shop')?->id,
'user_shop_domain' => session('user_shop_domain'),
'context_type' => $context->type,
'session_domain' => config('session.domain'),
'request_host' => request()->getHost()
]);
}
}
/**
* Prüft, ob diese Middleware für den aktuellen Request ausgeführt werden soll.
*
* @param Request $request
* @return bool
*/
private function shouldHandleRequest(Request $request): bool
{
// Überspringe API-Requests
if ($request->is('api/*')) {
return false;
}
// Überspringe Asset-Requests (CSS, JS, Bilder, etc.)
if ($request->isMethod('GET') && preg_match('/\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$/i', $request->path())) {
return false;
}
// Überspringe Laravel-interne Requests
if ($request->isMethod('GET') && in_array($request->path(), ['_debugbar/*'])) {
return false;
}
// Überspringe Health-Check und Monitoring Requests
if ($request->isMethod('GET') && in_array($request->path(), ['health', 'status', 'ping'])) {
return false;
}
return true;
}
}