206 lines
8.7 KiB
PHP
206 lines
8.7 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 je nach Kontext setzen
|
|
if ($context->type === 'shop') {
|
|
Config::set('session.domain', '.' . config('app.domain') . config('app.tld_shop'));
|
|
} else {
|
|
Config::set('session.domain', '.' . config('app.domain') . config('app.tld_care'));
|
|
}
|
|
// 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()
|
|
]);
|
|
}
|
|
// 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('domain')->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');
|
|
}
|
|
|
|
// Entferne subdomain Parameter aus der Route
|
|
// damit catch-all Routen wie /{site}/{subsite?}/{product_slug?} funktionieren
|
|
if ($request->route('subdomain')) {
|
|
$request->route()->forgetParameter('subdomain');
|
|
}
|
|
}
|
|
|
|
// Richtet den Anwendungskontext für Abwärtskompatibilität ein.
|
|
$this->setupLegacyContext($context);
|
|
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
|
|
*/
|
|
private function setupLegacyContext(DomainContext $context): void
|
|
{
|
|
// TODO: [TECH-DEBT] Diese Methode sollte langfristig entfernt werden.
|
|
// Alle Teile der Anwendung sollten den DomainContext direkt verwenden.
|
|
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::save();
|
|
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::save(); // Sofortige Session-Speicherung
|
|
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::save(); // Sofortige Session-Speicherung
|
|
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::save(); // Sofortige Session-Speicherung
|
|
// \Log::channel('domain')->debug('DomainResolver: user_shop_domain hinzugefügt (' . $context->type . ' domain)', ['user_shop' => session('user_shop')]);
|
|
Config::set('app.url', $context->host);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
}
|