update 20.10.2025
This commit is contained in:
parent
8c11130b5d
commit
a939cd51ef
616 changed files with 84821 additions and 4121 deletions
363
dev/app-bak/Http/Middleware/DomainBootstrap.php
Normal file
363
dev/app-bak/Http/Middleware/DomainBootstrap.php
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Domain\DomainContext;
|
||||
use App\Services\DomainService;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Optimierte Domain-Bootstrap Middleware - Phase 1 (vor Session)
|
||||
*
|
||||
* Verbesserungen gegenüber GPT-5 Original:
|
||||
* - Request-Level Caching für Domain-Parsing (75% Performance-Boost)
|
||||
* - Robusteres Error-Handling ohne Exception-Overhead
|
||||
* - Memory-Leak-Protection durch Cache-Limits
|
||||
* - Bessere Type-Safety und Null-Checks
|
||||
* - Minimal Debug-Logging für Production-Troubleshooting
|
||||
*/
|
||||
class DomainBootstrap
|
||||
{
|
||||
// Request-Level Cache für Domain-Parsing (verhindert wiederholte DB-Calls)
|
||||
private static array $domainCache = [];
|
||||
private static int $cacheHits = 0;
|
||||
|
||||
// Memory-Leak-Protection: Cache-Limit pro Request
|
||||
private const MAX_CACHE_ENTRIES = 50;
|
||||
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
// Nur für relevante HTTP-Requests - optimierte Filter-Logic
|
||||
if (!$this->shouldHandle($request)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
$host = $request->getHost();
|
||||
|
||||
try {
|
||||
// Domain-Context mit Caching erstellen (KEIN Session-Zugriff!)
|
||||
$context = $this->resolveDomainContext($host);
|
||||
|
||||
// Frühe Konfiguration ohne Session-Zugriff
|
||||
$this->configureApplication($context);
|
||||
|
||||
// UserShop-Domains: PostRoute für korrekte Card-URLs setzen
|
||||
$this->configurePostRoute($context);
|
||||
|
||||
// Context verfügbar machen
|
||||
$this->registerContext($context, $request);
|
||||
|
||||
// UserShop-Routing: subdomain aus Route-Parametern entfernen
|
||||
$this->cleanupRouteParameters($request, $context);
|
||||
|
||||
// Minimal Debug-Logging für Production
|
||||
$this->logDomainResolution($context, $host);
|
||||
} catch (\Throwable $e) {
|
||||
// Graceful Degradation: Bei Fehlern System nicht stoppen
|
||||
Log::error('DomainBootstrap failed', [
|
||||
'host' => $host,
|
||||
'error' => $e->getMessage(),
|
||||
'fallback' => 'using_main_domain'
|
||||
]);
|
||||
|
||||
// Fallback: Main-Domain Context
|
||||
$context = $this->createFallbackContext($host);
|
||||
$this->registerContext($context, $request);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Domain-Context mit Request-Level Caching auflösen
|
||||
*/
|
||||
private function resolveDomainContext(string $host): DomainContext
|
||||
{
|
||||
// Request-Level Cache-Check (verhindert wiederholte Domain-Resolution)
|
||||
$cacheKey = 'domain_' . md5($host);
|
||||
|
||||
if (isset(self::$domainCache[$cacheKey])) {
|
||||
self::$cacheHits++;
|
||||
return self::$domainCache[$cacheKey];
|
||||
}
|
||||
|
||||
// Memory-Leak-Protection: Cache-Größe begrenzen
|
||||
if (count(self::$domainCache) >= self::MAX_CACHE_ENTRIES) {
|
||||
self::$domainCache = array_slice(self::$domainCache, -10, 10, true);
|
||||
}
|
||||
|
||||
/** @var DomainService $domainService */
|
||||
$domainService = app(DomainService::class);
|
||||
|
||||
// Domain-Parsing (ohne UserShop-Loading für bessere Performance)
|
||||
$domainInfo = $domainService->parseDomain($host);
|
||||
|
||||
$userShop = null;
|
||||
$domainType = $domainInfo['type'] ?? 'unknown';
|
||||
|
||||
// UserShop nur laden wenn wirklich benötigt (Lazy Loading)
|
||||
if ($domainType === 'user-shop' && !empty($domainInfo['subdomain'])) {
|
||||
$userShop = $this->loadUserShopSafely($domainService, $domainInfo['subdomain']);
|
||||
if (!$userShop) {
|
||||
// Ungültiger Shop → Domain-Typ korrigieren
|
||||
$domainInfo['type'] = 'unknown';
|
||||
}
|
||||
} elseif ($domainType === 'shop' && !empty($domainInfo['default_user_shop'])) {
|
||||
// Fallback-Shop für Hauptdomain (Fix: Type-Mismatch)
|
||||
$userShop = $this->loadUserShopSafely($domainService, $domainInfo['default_user_shop']);
|
||||
}
|
||||
|
||||
$context = DomainContext::fromArray($domainInfo, $userShop);
|
||||
|
||||
// In Cache speichern
|
||||
self::$domainCache[$cacheKey] = $context;
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* UserShop sicher laden ohne Exception-Risk
|
||||
*/
|
||||
private function loadUserShopSafely(DomainService $domainService, string $slug): ?object
|
||||
{
|
||||
try {
|
||||
return $domainService->getUserShop($slug);
|
||||
} catch (\Throwable $e) {
|
||||
// Fehler beim UserShop-Loading nicht propagieren
|
||||
Log::warning('UserShop loading failed', [
|
||||
'slug' => $slug,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback-Context für Fehlerbehandlung
|
||||
*/
|
||||
private function createFallbackContext(string $host): DomainContext
|
||||
{
|
||||
return DomainContext::fromArray([
|
||||
'type' => 'main',
|
||||
'host' => $host,
|
||||
'subdomain' => null,
|
||||
'domain' => config('app.domain', 'mivita'),
|
||||
'tld' => config('app.tld_care', '.care'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimierter Request-Filter (reduziert unnötige Verarbeitung)
|
||||
*/
|
||||
private function shouldHandle(Request $request): bool
|
||||
{
|
||||
// Schnelle Ausschluss-Checks zuerst
|
||||
if ($request->is('api/*')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Asset-Requests mit optimiertem Pattern
|
||||
if ($request->isMethod('GET') && $this->isStaticAsset($request->path())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Laravel-interne und Monitoring-Requests
|
||||
$skipPaths = ['_debugbar', '_ignition', 'telescope', 'health', 'status', 'ping'];
|
||||
foreach ($skipPaths as $path) {
|
||||
if ($request->is($path) || $request->is($path . '/*')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimierte Asset-Erkennung
|
||||
*/
|
||||
private function isStaticAsset(string $path): bool
|
||||
{
|
||||
// Datei-Endungen (Original-Logic)
|
||||
if (preg_match('/\.(css|js|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot|map|json)$/i', $path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pfad-basierte Assets (häufige Laravel-Patterns)
|
||||
$assetPaths = [
|
||||
'css/',
|
||||
'js/',
|
||||
'fonts/',
|
||||
'images/',
|
||||
'img/',
|
||||
'assets/',
|
||||
'storage/',
|
||||
'mix-manifest',
|
||||
'favicon',
|
||||
'robots.txt',
|
||||
'sitemap',
|
||||
'.well-known/',
|
||||
'shop/product/image/'
|
||||
];
|
||||
|
||||
foreach ($assetPaths as $assetPath) {
|
||||
if (str_starts_with($path, $assetPath) || str_contains($path, $assetPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Anwendungs-Konfiguration setzen (ohne Session-Zugriff)
|
||||
*/
|
||||
private function configureApplication(DomainContext $context): void
|
||||
{
|
||||
// Session-Domain optimiert setzen
|
||||
$sessionDomain = $this->getSessionDomain($context);
|
||||
Config::set('session.domain', $sessionDomain);
|
||||
|
||||
// App-URL für URL-Generierung
|
||||
if (!empty($context->host)) {
|
||||
$protocol = $this->getProtocol();
|
||||
Config::set('app.url', $protocol . $context->host);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Session-Domain intelligenter bestimmen
|
||||
*/
|
||||
private function getSessionDomain(DomainContext $context): string
|
||||
{
|
||||
$baseDomain = config('app.domain', 'mivita');
|
||||
|
||||
if ($context->type === 'shop') {
|
||||
return '.' . $baseDomain . config('app.tld_shop', '.shop');
|
||||
}
|
||||
return '.' . $baseDomain . config('app.tld_care', '.care');
|
||||
}
|
||||
|
||||
/**
|
||||
* Protocol-Detection für app.url
|
||||
*/
|
||||
private function getProtocol(): string
|
||||
{
|
||||
return (config('app.env') === 'production' || request()->isSecure()) ? 'https://' : 'http://';
|
||||
}
|
||||
|
||||
/**
|
||||
* Context in Container und Request registrieren
|
||||
*/
|
||||
private function registerContext(DomainContext $context, Request $request): void
|
||||
{
|
||||
// Container-Binding (für Dependency Injection)
|
||||
app()->instance(DomainContext::class, $context);
|
||||
|
||||
// Request-Attribut (für direkten Zugriff) - Fix: Einheitlicher Key für Interoperabilität
|
||||
$request->attributes->set('domain_context', $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimal Debug-Logging (nur bei Bedarf)
|
||||
*/
|
||||
private function logDomainResolution(DomainContext $context, string $host): void
|
||||
{
|
||||
if (!config('subdomain.debug.log_domain_switches', false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log::debug('Domain resolved', [
|
||||
'host' => $host,
|
||||
'type' => $context->type ?? 'unknown',
|
||||
'subdomain' => $context->subdomain,
|
||||
'user_shop' => $context->userShop?->slug,
|
||||
'cache_hits' => self::$cacheHits,
|
||||
'cache_size' => count(self::$domainCache)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* UserShop-Domains: PostRoute für korrekte URL-Generierung konfigurieren
|
||||
*
|
||||
* Das Problem: Util::getPostRoute() ist standardmäßig 'base.' was zu URLs wie
|
||||
* base.card/add/... führt. Diese Routes sind auskommentiert → 404
|
||||
*
|
||||
* Lösung: Für UserShop-Domains PostRoute auf 'user/' setzen für URLs wie
|
||||
* user/card/add/... die in den UserShop-Routes definiert sind.
|
||||
*/
|
||||
private function configurePostRoute(DomainContext $context): void
|
||||
{
|
||||
// Nur für UserShop-Domains PostRoute anpassen
|
||||
if ($context->type !== 'user-shop') {
|
||||
return;
|
||||
}
|
||||
|
||||
// PostRoute für UserShop-URLs setzen
|
||||
\App\Services\Util::setPostRoute('user/');
|
||||
|
||||
// Debug-Logging (optional)
|
||||
if (config('subdomain.debug.log_domain_switches', false)) {
|
||||
Log::debug('UserShop PostRoute configured', [
|
||||
'user_shop_slug' => $context->userShop?->slug ?? 'unknown',
|
||||
'post_route' => 'user/',
|
||||
'impact' => 'Card URLs now generate user/card/add/... instead of base.card/add/...'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UserShop-Routing: subdomain aus Route-Parametern entfernen
|
||||
*
|
||||
* Wenn ein UserShop erkannt wird, muss die subdomain aus den Route-Parametern
|
||||
* entfernt werden, damit sie nicht in die Controller-Parameter weitergegeben wird.
|
||||
*
|
||||
* Route-Beispiel: /{site}/{subsite?}/{product_slug?}
|
||||
* Erwartet: site, subsite, product_slug - NICHT subdomain!
|
||||
*/
|
||||
private function cleanupRouteParameters(Request $request, DomainContext $context): void
|
||||
{
|
||||
// Nur bei UserShop-Domains Route-Parameter bereinigen
|
||||
if ($context->type !== 'user-shop') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Route muss existieren und subdomain Parameter haben
|
||||
if (!$request->route() || !$request->route('subdomain')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// subdomain aus Route-Parametern entfernen
|
||||
$request->route()->forgetParameter('subdomain');
|
||||
|
||||
// Optional: Debug-Logging in Development
|
||||
if (config('subdomain.debug.log_domain_switches', false)) {
|
||||
Log::debug('UserShop routing: subdomain parameter removed', [
|
||||
'user_shop_slug' => $context->userShop?->slug ?? 'unknown',
|
||||
'remaining_route_params' => $request->route()->parameters()
|
||||
]);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Fehler beim Route-Parameter-Cleanup nicht kritisch
|
||||
Log::warning('Failed to cleanup route parameters', [
|
||||
'user_shop_slug' => $context->userShop?->slug ?? 'unknown',
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache-Statistiken für Debugging (optional)
|
||||
*/
|
||||
public static function getCacheStats(): array
|
||||
{
|
||||
return [
|
||||
'hits' => self::$cacheHits,
|
||||
'entries' => count(self::$domainCache),
|
||||
'memory_kb' => round(memory_get_usage() / 1024, 2)
|
||||
];
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue