update 20.10.2025

This commit is contained in:
Kevin Adametz 2025-10-20 17:42:08 +02:00
parent 8c11130b5d
commit a939cd51ef
616 changed files with 84821 additions and 4121 deletions

View file

@ -0,0 +1,73 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
/**
* Debuggt Session-Änderungen vor AuthenticateSession.
*
* Diese Middleware läuft direkt vor Illuminate\Session\Middleware\AuthenticateSession
* und überprüft, ob die Session-ID sich ändert.
*/
class AuthSessionDebugger
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
// Session-ID vor AuthenticateSession überprüfen
$sessionIdBeforeAuth = Session::getId();
$domainResolverSessionId = $request->attributes->get('domain_resolver_session_id');
if (config('app.debug')) {
\Log::channel('domain')->debug('AuthSessionDebugger: VOR AuthenticateSession', [
'session_id_before_auth' => $sessionIdBeforeAuth,
'domain_resolver_session_id' => $domainResolverSessionId,
'session_consistent_with_domain_resolver' => $domainResolverSessionId === $sessionIdBeforeAuth,
'session_started' => Session::isStarted(),
'request_host' => $request->getHost(),
'middleware_position' => 'Vor AuthenticateSession',
'user_authenticated' => Auth::check(),
'session_all_keys' => array_keys(Session::all())
]);
}
// Request weiterleiten (AuthenticateSession läuft hier)
$response = $next($request);
// Session-ID nach AuthenticateSession vergleichen
$sessionIdAfterAuth = Session::getId();
if (config('app.debug')) {
\Log::channel('domain')->debug('AuthSessionDebugger: NACH AuthenticateSession', [
'session_id_before_auth' => $sessionIdBeforeAuth,
'session_id_after_auth' => $sessionIdAfterAuth,
'session_changed_by_auth' => $sessionIdBeforeAuth !== $sessionIdAfterAuth,
'domain_resolver_session_id' => $domainResolverSessionId,
'request_host' => $request->getHost(),
'user_authenticated' => auth()->check()
]);
if ($sessionIdBeforeAuth !== $sessionIdAfterAuth) {
\Log::channel('domain')->warning('🚨 AuthSessionDebugger: AuthenticateSession hat Session-ID geändert!', [
'session_id_before' => $sessionIdBeforeAuth,
'session_id_after' => $sessionIdAfterAuth,
'domain_resolver_session_id' => $domainResolverSessionId,
'request_host' => $request->getHost(),
'user_agent' => $request->userAgent(),
'possible_cause' => 'AuthenticateSession regeneriert Session bei Authentifizierungsproblemen'
]);
}
}
return $response;
}
}

View file

@ -0,0 +1,290 @@
<?php
namespace App\Domain;
use App\Models\UserShop;
/**
* Domain Context - Immutable context object representing current domain state
*
* This class provides a clean, immutable way to pass domain context
* throughout the application without relying on global state.
*/
class DomainContext
{
public function __construct(
public readonly string $type, // 'main', 'main-shop', 'crm', 'portal', 'checkout', 'user-shop', 'unknown'
public readonly string $fullDomain, // Complete domain (e.g., 'shop.mivita.care')
public readonly string $domain, // Base domain (e.g., 'mivita')
public readonly ?string $subdomain, // Subdomain part (e.g., 'shop') or null
public readonly string $tld, // TLD (e.g., '.care')
public readonly ?UserShop $userShop = null, // For user shop contexts
public readonly array $config = [] // Additional configuration
) {}
/**
* Check if this is the main domain (mivita.care)
*/
public function isMainDomain(): bool
{
return $this->type === 'main';
}
/**
* Check if this is the main shop domain (mivita.shop)
*/
public function isMainShopDomain(): bool
{
return $this->type === 'main-shop';
}
/**
* Check if this is any main domain variant
*/
public function isAnyMainDomain(): bool
{
return $this->isMainDomain() || $this->isMainShopDomain();
}
/**
* Check if this is the CRM domain (my.mivita.care)
*/
public function isCrmDomain(): bool
{
return $this->type === 'crm';
}
/**
* Check if this is the portal domain (in.mivita.care)
*/
public function isPortalDomain(): bool
{
return $this->type === 'portal';
}
/**
* Check if this is the checkout domain (checkout.mivita.care)
*/
public function isCheckoutDomain(): bool
{
return $this->type === 'checkout';
}
/**
* Check if this is a user shop domain ({slug}.mivita.care)
*/
public function isUserShopDomain(): bool
{
return $this->type === 'user-shop';
}
/**
* Check if this is an unknown/invalid domain
*/
public function isUnknownDomain(): bool
{
return $this->type === 'unknown';
}
/**
* Check if this domain supports e-commerce functionality
*/
public function supportsEcommerce(): bool
{
return in_array($this->type, ['main-shop', 'user-shop']);
}
/**
* Check if this domain requires authentication
*/
public function requiresAuthentication(): bool
{
return in_array($this->type, ['crm', 'portal']);
}
/**
* Get the appropriate route prefix for this domain
*/
public function getRoutePrefix(): ?string
{
return match($this->type) {
'crm' => 'crm',
'portal' => 'portal',
'checkout' => 'checkout',
'user-shop' => 'shop',
default => null
};
}
/**
* Get the user shop slug (if applicable)
*/
public function getUserShopSlug(): ?string
{
return $this->userShop?->slug ?? $this->subdomain;
}
/**
* Get the middleware stack appropriate for this domain
*/
public function getMiddlewareStack(): array
{
$middleware = ['web']; // Base middleware
switch ($this->type) {
case 'crm':
$middleware[] = 'auth';
break;
case 'portal':
$middleware[] = 'auth:customers';
break;
case 'checkout':
$middleware[] = 'checkout';
break;
case 'user-shop':
$middleware[] = 'subdomain';
break;
}
return $middleware;
}
/**
* Get the view namespace for this domain
*/
public function getViewNamespace(): ?string
{
return match($this->type) {
'crm' => 'crm',
'portal' => 'portal',
'checkout' => 'checkout',
'user-shop' => 'shop',
default => null
};
}
/**
* Get the layout template for this domain
*/
public function getLayoutTemplate(): string
{
return match($this->type) {
'crm' => 'layouts.crm',
'portal' => 'layouts.portal',
'checkout' => 'layouts.checkout',
'user-shop' => 'web.layouts.application',
'main-shop' => 'web.layouts.application',
default => 'layouts.app'
};
}
/**
* Check if this domain allows cart functionality
*/
public function allowsCart(): bool
{
return $this->supportsEcommerce();
}
/**
* Get session configuration for this domain
*/
public function getSessionConfig(): array
{
$config = [
'lifetime' => config('session.lifetime'),
'expire_on_close' => config('session.expire_on_close'),
];
// Set domain-specific session domain
if ($this->isUserShopDomain()) {
$config['domain'] = '.' . $this->domain . $this->tld;
}
return $config;
}
/**
* Create a new context with updated user shop
*/
public function withUserShop(UserShop $userShop): self
{
return new self(
$this->type,
$this->fullDomain,
$this->domain,
$this->subdomain,
$this->tld,
$userShop,
$this->config
);
}
/**
* Create a new context with additional configuration
*/
public function withConfig(array $config): self
{
return new self(
$this->type,
$this->fullDomain,
$this->domain,
$this->subdomain,
$this->tld,
$this->userShop,
array_merge($this->config, $config)
);
}
/**
* Convert to array for debugging/logging
*/
public function toArray(): array
{
return [
'type' => $this->type,
'full_domain' => $this->fullDomain,
'domain' => $this->domain,
'subdomain' => $this->subdomain,
'tld' => $this->tld,
'user_shop_slug' => $this->getUserShopSlug(),
'user_shop_id' => $this->userShop?->id,
'config' => $this->config,
];
}
/**
* Get a human-readable description of this domain context
*/
public function getDescription(): string
{
return match($this->type) {
'main' => 'Main Website',
'main-shop' => 'Main Shop',
'crm' => 'CRM System',
'portal' => 'Customer Portal',
'checkout' => 'Checkout System',
'user-shop' => "User Shop ({$this->getUserShopSlug()})",
'unknown' => 'Unknown Domain',
default => 'Undefined Domain Type'
};
}
/**
* Static factory method to create context from domain service
*/
public static function fromDomainInfo(array $domainInfo, ?UserShop $userShop = null): self
{
return new self(
$domainInfo['type'],
$domainInfo['full_domain'],
$domainInfo['domain'],
$domainInfo['subdomain'],
$domainInfo['tld'],
$userShop
);
}
}

View file

@ -0,0 +1,206 @@
<?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;
}
}

View file

@ -0,0 +1,284 @@
<?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;
}
}

View file

@ -0,0 +1,86 @@
<?php
namespace App\Providers;
use App\Domain\DomainContext;
use App\Http\Middleware\DomainResolver;
use App\Models\UserShop;
use App\Services\DomainService;
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Support\ServiceProvider;
class DomainServiceProvider extends ServiceProvider
{
/**
* Registriert die Domain-Dienste im Service-Container.
*
* @return void
*/
public function register()
{
// 1. DomainService als Singleton registrieren.
// Er wird die Konfiguration `config/domains.php` verwenden.
$this->app->singleton(DomainService::class, function ($app) {
$domainService = new DomainService($app['config']['domains']);
// Validiere Konfiguration in der Development-Umgebung
if (config('app.debug')) {
$configErrors = $domainService->validateConfiguration();
if (!empty($configErrors)) {
\Log::channel('domain')->warning('Domain configuration errors detected', ['errors' => $configErrors]);
}
}
return $domainService;
});
// 2. DomainContext als Singleton registrieren.
// Die Logik hier wird nur einmal pro Anfrage ausgeführt, wenn der
// Context das erste Mal benötigt wird (z.B. in der Middleware).
$this->app->singleton(DomainContext::class, function ($app) {
/** @var DomainService $domainService */
$domainService = $app->make(DomainService::class);
$request = $app->make('request');
// Analysiere den Host der aktuellen Anfrage
$domainInfo = $domainService->parseDomain($request->getHost());
if (config('app.debug')) {
\Log::channel('domain')->debug('DomainServiceProvider: domainInfo', [
'domainInfo' => $domainInfo,
'host' => $request->getHost()
]);
}
$userShop = null;
// Wenn es sich um eine User-Shop-Domain handelt, versuche das Shop-Objekt zu finden.
if ($domainInfo['type'] === 'user-shop' && $domainInfo['subdomain']) {
$userShop = $domainService->getUserShop($domainInfo['subdomain']);
// Wenn der Shop ungültig ist, wird der Typ auf 'unknown' gesetzt.
if (!$userShop) {
$domainInfo['type'] = 'unknown';
}
}
// Wenn es sich um die Haupt-Shop-Domain handelt (z.B. mivita.shop),
// lade den konfigurierten Fallback-Shop.
if ($domainInfo['type'] === 'main-shop' && $domainInfo['default_user_shop']) {
$userShop = $domainService->getUserShop($domainInfo['default_user_shop']);
}
return DomainContext::fromArray($domainInfo, $userShop);
});
}
/**
* Führt Aktionen nach der Registrierung aller Provider aus.
*
* @return void
*/
public function boot(Kernel $kernel)
{
// Registriert die neue Middleware global für die 'web' Gruppe.
// Sie wird vor anderen Middleware ausgeführt, um den Kontext frühzeitig zu setzen.
$kernel = $this->app->make(\Illuminate\Contracts\Http\Kernel::class);
$kernel->prependMiddlewareToGroup('web', DomainResolver::class);
}
}

View file

@ -0,0 +1,233 @@
# Mivita Multi-Domain & Subdomain Architecture Optimization
## Current Implementation Analysis
### Domain Structure Overview
The mivita.care application uses a multi-domain and subdomain architecture to serve different application areas:
#### Main Domains & Subdomains:
1. **Main Domain**: `mivita.care` (or `mivita.test` in development)
2. **Fixed Subdomains**:
- `my.mivita.care` - CRM/Customer Management System
- `in.mivita.care` - Portal for customers
- `checkout.mivita.care` - Checkout/Payment processing
3. **Dynamic Subdomains**: `{shop-slug}.mivita.care` - Individual user shops
4. **Alternative Domain**: `mivita.shop` - Alternative shopping domain
### Current Architecture Issues
#### 1. Subdomain Middleware Problems
**File**: `app/Http/Middleware/Subdomain.php`
**Issues Identified**:
- Hard-coded shop selection (`'aloevera'`) for main domain
- Mixed responsibility (handles both dynamic shops and main domain logic)
- No proper fallback mechanism for invalid subdomains
- Configuration dependencies scattered across middleware
- Session management directly in middleware
#### 2. Routing Complexity
**Current Structure**:
```
routes/
├── web.php # Main entry (mostly empty)
├── main.php # Main domain routes
├── subdomain.php # Dynamic subdomain routes
├── crm.php # CRM subdomain (my.)
├── portal.php # Portal subdomain (in.)
├── checkout.php # Checkout subdomain (checkout.)
├── api.php # API routes
└── utility.php # Utility routes
```
**Issues**:
- Route duplication across files
- No clear separation of concerns
- Complex domain-based routing in multiple files
- Inconsistent middleware application
#### 3. Configuration Management
**File**: `.env`
**Current Setup**:
```
APP_DOMAIN=mivita
APP_TLD_CARE=.test
APP_TLD_SHOP=.lshop
APP_URL_CHECKOUT=checkout.
APP_URL_CRM=my.
APP_URL_PORTAL=in.
```
**Issues**:
- Environment-dependent configuration
- No centralized domain management
- Missing validation for domain configurations
## Optimization Proposal
### 1. Enhanced Subdomain Middleware
Create a new, more robust subdomain middleware system:
```php
// app/Http/Middleware/DomainResolver.php
class DomainResolver
{
public function handle($request, Closure $next)
{
$domain = $this->resolveDomain($request);
// Set domain context
app()->instance('domain.context', $domain);
return $next($request);
}
private function resolveDomain($request): DomainContext
{
// Logic to determine domain type and set appropriate context
}
}
```
### 2. Domain Configuration Service
```php
// app/Services/DomainService.php
class DomainService
{
public function getSubdomainType(string $subdomain): string
{
// Determine if subdomain is fixed (my, in, checkout) or dynamic (user shop)
}
public function isValidUserShop(string $slug): bool
{
// Validate user shop existence and status
}
public function getDomainConfiguration(): array
{
// Return centralized domain configuration
}
}
```
### 3. Improved Route Organization
```
routes/
├── web.php # Route registration orchestrator
├── domains/
│ ├── main.php # Main domain (mivita.care)
│ ├── shop.php # Alternative domain (mivita.shop)
│ └── subdomains/
│ ├── crm.php # my.mivita.care
│ ├── portal.php # in.mivita.care
│ ├── checkout.php # checkout.mivita.care
│ └── user-shops.php # {slug}.mivita.care
├── api/
│ └── v1.php # API routes
└── shared/
├── legal.php # Legal pages (shared across domains)
└── common.php # Common functionality
```
### 4. Domain Context System
```php
// app/Domain/DomainContext.php
class DomainContext
{
public function __construct(
public readonly string $type, // 'main', 'crm', 'portal', 'checkout', 'user-shop'
public readonly string $domain, // Full domain
public readonly ?string $subdomain, // Subdomain part
public readonly ?UserShop $userShop // For user shop contexts
) {}
public function isMainDomain(): bool
public function isCrmDomain(): bool
public function isPortalDomain(): bool
public function isCheckoutDomain(): bool
public function isUserShopDomain(): bool
}
```
## Implementation Benefits
### 1. Performance Improvements
- **Reduced Database Queries**: Cache user shop validity
- **Faster Route Resolution**: Dedicated route files per domain type
- **Optimized Middleware Stack**: Domain-specific middleware application
### 2. Maintainability
- **Clear Separation of Concerns**: Each domain type has its own route file
- **Centralized Configuration**: Single source of truth for domain settings
- **Consistent Architecture**: Uniform handling across all domain types
### 3. Scalability
- **Easy Subdomain Addition**: New subdomains can be added without touching existing code
- **Flexible Configuration**: Environment-agnostic domain management
- **Modular Structure**: Independent development of domain-specific features
### 4. Security Enhancements
- **Domain Validation**: Proper validation of all subdomain requests
- **Context Isolation**: Clear boundaries between different application areas
- **CSRF Protection**: Domain-aware CSRF token handling
## Migration Strategy
### Phase 1: Foundation
1. Create new `DomainService` and `DomainContext` classes
2. Implement enhanced `DomainResolver` middleware
3. Add domain configuration management
### Phase 2: Route Reorganization
1. Create new route structure under `routes/domains/`
2. Migrate existing routes to new organization
3. Update route service provider
### Phase 3: Testing & Optimization
1. Comprehensive testing of all domain types
2. Performance optimization and caching
3. Documentation updates
### Phase 4: Deployment
1. Gradual rollout with fallback mechanisms
2. Monitor performance and functionality
3. Remove old code after successful migration
## Risk Assessment
### Low Risk
- Domain service implementation
- Route reorganization
- Configuration centralization
### Medium Risk
- Middleware replacement (affects all requests)
- Session handling changes
- Cache invalidation
### High Risk
- User shop subdomain handling (affects customer shops)
- Payment domain changes (affects revenue)
## Monitoring & Rollback Plan
### Monitoring Points
- Response times per domain type
- Error rates by subdomain
- User shop accessibility
- Payment processing success rates
### Rollback Strategy
- Feature flags for gradual enablement
- Database transaction rollbacks for configuration changes
- Immediate fallback to current middleware if critical issues arise
## Conclusion
This optimization will provide a more maintainable, scalable, and performant multi-domain architecture while preserving all existing functionality. The modular approach allows for incremental implementation and testing, reducing deployment risks.

View file

@ -0,0 +1,63 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
/**
* Bereinigt Route-Parameter nach der Domain-Auflösung.
*
* Diese Middleware läuft nach der Route-Auflösung und entfernt
* unnötige Parameter wie 'subdomain' aus catch-all Routen.
*/
class RouteCleanup
{
/**
* Behandelt eine eingehende Anfrage.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
// Session-ID vor RouteCleanup überprüfen
$sessionIdBeforeCleanup = \Session::isStarted() ? \Session::getId() : 'session_not_started';
// Entferne subdomain Parameter aus der Route
// damit catch-all Routen wie /{site}/{subsite?}/{product_slug?} funktionieren
if ($request->route() && $request->route('subdomain')) {
$request->route()->forgetParameter('subdomain');
if (config('app.debug')) {
\Log::channel('domain')->debug('RouteCleanup: subdomain Parameter aus Route entfernt', [
'route_name' => $request->route()->getName(),
'route_uri' => $request->route()->uri(),
'remaining_parameters' => array_keys($request->route()->parameters()),
'request_path' => $request->path(),
'request_host' => $request->getHost(),
'session_id_before_cleanup' => $sessionIdBeforeCleanup
]);
}
}
// Request weiterleiten
$response = $next($request);
// Session-ID nach RouteCleanup vergleichen
$sessionIdAfterCleanup = \Session::isStarted() ? \Session::getId() : 'session_not_started';
if (config('app.debug') && $sessionIdBeforeCleanup !== $sessionIdAfterCleanup) {
\Log::channel('domain')->warning('🚨 RouteCleanup: Session-ID hat sich geändert!', [
'session_id_before' => $sessionIdBeforeCleanup,
'session_id_after' => $sessionIdAfterCleanup,
'request_path' => $request->path(),
'request_host' => $request->getHost(),
'route_name' => $request->route() ? $request->route()->getName() : 'no_route'
]);
}
return $response;
}
}

View file

@ -0,0 +1,344 @@
<?php
namespace App\Providers;
use App\Domain\DomainContext;
use App\Services\DomainService;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
/**
* Enhanced Route Service Provider
*
* This service provider implements the new domain-aware routing system
* that loads routes based on the current domain context.
*/
class RouteServiceProvider extends ServiceProvider
{
/**
* The path to the "home" route for your application.
*/
public const HOME = '/home';
/**
* Define your route model bindings, pattern filters, etc.
*/
public function boot()
{
$this->configureRateLimiting();
$this->routes(function () {
// Load API routes first (domain-independent)
Route::prefix('api')
->middleware('api')
->group(base_path('routes/api.php'));
// Load web routes with domain awareness
Route::middleware('web')
->group(function () {
$this->loadDomainAwareRoutes();
});
});
}
/**
* Load routes based on current domain context
*/
protected function loadDomainAwareRoutes(): void
{
// Try to get domain context (might not be available during route caching)
$domainContext = $this->getDomainContext();
// Load shared routes first (always available)
$this->loadSharedRoutes();
// Load domain-specific routes if context is available
if ($domainContext) {
$this->loadDomainSpecificRoutes($domainContext);
} else {
// Fallback: load all routes for route caching or when context is unavailable
$this->loadAllRoutes();
}
}
/**
* Get current domain context
*/
protected function getDomainContext(): ?DomainContext
{
try {
return app('domain.context');
} catch (\Exception $e) {
// Context not available (e.g., during route caching)
return null;
}
}
/**
* Load shared routes that are available across all domains
*/
protected function loadSharedRoutes(): void
{
// Legal pages (datenschutz, impressum, agb)
Route::group([], base_path('routes/shared/legal.php'));
// Common functionality (health checks, modals, etc.)
Route::group([], base_path('routes/shared/common.php'));
}
/**
* Load routes specific to the current domain
*/
protected function loadDomainSpecificRoutes(DomainContext $context): void
{
match ($context->type) {
'main' => $this->loadMainDomainRoutes(),
'main-shop' => $this->loadShopDomainRoutes(),
'crm' => $this->loadCrmDomainRoutes(),
'portal' => $this->loadPortalDomainRoutes(),
'checkout' => $this->loadCheckoutDomainRoutes(),
'user-shop' => $this->loadUserShopDomainRoutes(),
default => null // Unknown domains are handled by middleware
};
}
/**
* Load all routes (fallback for route caching)
*/
protected function loadAllRoutes(): void
{
// Load all domain-specific routes
$this->loadMainDomainRoutes();
$this->loadShopDomainRoutes();
$this->loadCrmDomainRoutes();
$this->loadPortalDomainRoutes();
$this->loadCheckoutDomainRoutes();
$this->loadUserShopDomainRoutes();
}
/**
* Load main domain routes (mivita.care)
*/
protected function loadMainDomainRoutes(): void
{
Route::group([
'middleware' => ['domain.resolver'],
'domain' => $this->getMainDomain(),
], base_path('routes/domains/main.php'));
}
/**
* Load shop domain routes (mivita.shop)
*/
protected function loadShopDomainRoutes(): void
{
Route::group([
'middleware' => ['domain.resolver'],
'domain' => $this->getShopDomain(),
], base_path('routes/domains/shop.php'));
}
/**
* Load CRM domain routes (my.mivita.care)
*/
protected function loadCrmDomainRoutes(): void
{
Route::group([
'middleware' => ['domain.resolver'],
'domain' => $this->getCrmDomain(),
], base_path('routes/domains/subdomains/crm.php'));
}
/**
* Load portal domain routes (in.mivita.care)
*/
protected function loadPortalDomainRoutes(): void
{
Route::group([
'middleware' => ['domain.resolver'],
'domain' => $this->getPortalDomain(),
], base_path('routes/domains/subdomains/portal.php'));
}
/**
* Load checkout domain routes (checkout.mivita.care)
*/
protected function loadCheckoutDomainRoutes(): void
{
Route::group([
'middleware' => ['domain.resolver'],
'domain' => $this->getCheckoutDomain(),
], base_path('routes/domains/subdomains/checkout.php'));
}
/**
* Load user shop domain routes ({slug}.mivita.care)
*/
protected function loadUserShopDomainRoutes(): void
{
Route::group([
'middleware' => ['domain.resolver'],
'domain' => $this->getUserShopDomain(),
], base_path('routes/domains/subdomains/user-shops.php'));
}
/**
* Get main domain pattern
*/
protected function getMainDomain(): string
{
$domain = config('app.domain', 'mivita');
$tld = config('app.tld_care', '.care');
return $domain . $tld;
}
/**
* Get shop domain pattern
*/
protected function getShopDomain(): string
{
$domain = config('app.domain', 'mivita');
$tld = config('app.tld_shop', '.shop');
return $domain . $tld;
}
/**
* Get CRM domain pattern
*/
protected function getCrmDomain(): string
{
$subdomain = rtrim(config('app.pre_url_crm', 'my.'), '.');
$domain = config('app.domain', 'mivita');
$tld = config('app.tld_care', '.care');
return $subdomain . '.' . $domain . $tld;
}
/**
* Get portal domain pattern
*/
protected function getPortalDomain(): string
{
$subdomain = rtrim(config('app.pre_url_portal', 'in.'), '.');
$domain = config('app.domain', 'mivita');
$tld = config('app.tld_care', '.care');
return $subdomain . '.' . $domain . $tld;
}
/**
* Get checkout domain pattern
*/
protected function getCheckoutDomain(): string
{
$subdomain = rtrim(config('app.pre_url_checkout', 'checkout.'), '.');
$domain = config('app.domain', 'mivita');
$tld = config('app.tld_care', '.care');
return $subdomain . '.' . $domain . $tld;
}
/**
* Get user shop domain pattern (wildcard subdomain)
*/
protected function getUserShopDomain(): string
{
$domain = config('app.domain', 'mivita');
$tld = config('app.tld_care', '.care');
return '{subdomain}.' . $domain . $tld;
}
/**
* Configure the rate limiters for the application
*/
protected function configureRateLimiting()
{
// API rate limiting
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
// Domain-specific rate limiting
RateLimiter::for('crm', function (Request $request) {
return Limit::perMinute(120)->by($request->user()?->id ?: $request->ip());
});
RateLimiter::for('portal', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
RateLimiter::for('checkout', function (Request $request) {
return Limit::perMinute(30)->by($request->ip());
});
RateLimiter::for('user-shop', function (Request $request) {
return Limit::perMinute(100)->by($request->ip());
});
// Login rate limiting
RateLimiter::for('login', function (Request $request) {
return [
Limit::perMinute(5)->by($request->email . $request->ip()),
Limit::perMinute(10)->by($request->ip()),
];
});
}
/**
* Get route caching path based on domain
*/
public function getCachedRoutesPath()
{
// Use different cache files for different environments
$environment = app()->environment();
return $this->app->bootstrapPath("cache/routes-v7-{$environment}.php");
}
/**
* Determine if routes are cached
*/
public function routesAreCached()
{
return $this->app->routesAreCached();
}
/**
* Load the cached routes for the application
*/
public function loadCachedRoutes()
{
$this->app->booted(function () {
require $this->getCachedRoutesPath();
});
}
/**
* Register domain-specific route macros
*/
protected function registerRouteMacros(): void
{
// Macro for domain-aware redirects
Route::macro('domainRedirect', function (string $domainType, string $path = '/', int $status = 302) {
return function () use ($domainType, $path, $status) {
$domainService = app(DomainService::class);
$url = $domainService->buildUrl($domainType, $path);
return redirect($url, $status);
};
});
// Macro for user shop routes
Route::macro('userShop', function (callable $callback) {
return Route::group([
'middleware' => ['domain.resolver', 'user-shop.validate'],
], $callback);
});
// Macro for CRM routes
Route::macro('crm', function (callable $callback) {
return Route::group([
'middleware' => ['domain.resolver', 'auth'],
'prefix' => '',
], $callback);
});
}
}

View file

@ -0,0 +1,69 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session;
use Symfony\Component\HttpFoundation\Response;
/**
* Debuggt Session-Änderungen direkt nach StartSession.
*
* Diese Middleware läuft direkt nach Illuminate\Session\Middleware\StartSession
* und überprüft, ob die Session-ID sich geändert hat.
*/
class SessionDebugger
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
// Session-ID nach StartSession überprüfen
$currentSessionId = Session::getId();
if (config('app.debug')) {
\Log::channel('domain')->debug('SessionDebugger: Direkt nach StartSession', [
'session_id_after_start_session' => $currentSessionId,
'session_started' => Session::isStarted(),
'session_domain' => config('session.domain'),
'request_host' => $request->getHost(),
'middleware_position' => 'Nach StartSession, vor AuthSessionDebugger',
'session_all_keys' => array_keys(Session::all())
]);
}
// Vergleiche mit der DomainResolver Session-ID (wenn verfügbar)
$domainResolverSessionId = $request->attributes->get('domain_resolver_session_id');
if ($domainResolverSessionId && config('app.debug')) {
if ($domainResolverSessionId !== $currentSessionId) {
\Log::channel('domain')->warning('🚨 SessionDebugger: Session-ID unterscheidet sich von DomainResolver!', [
'domain_resolver_session_id' => $domainResolverSessionId,
'current_session_id' => $currentSessionId,
'session_regenerated_by' => 'StartSession - Domain-Konfiguration war falsch!',
'request_host' => $request->getHost(),
'session_domain' => config('session.domain'),
'solution' => 'DomainResolver setzt jetzt korrekte Session-Domain vor StartSession'
]);
} else {
\Log::channel('domain')->info('✅ SessionDebugger: Session-ID konsistent mit DomainResolver', [
'session_id' => $currentSessionId,
'request_host' => $request->getHost(),
'session_domain' => config('session.domain'),
'status' => 'Session-Domain wurde korrekt vor StartSession gesetzt'
]);
}
} elseif (config('app.debug')) {
\Log::channel('domain')->debug('SessionDebugger: Keine DomainResolver Session-ID zum Vergleich verfügbar', [
'current_session_id' => $currentSessionId,
'request_host' => $request->getHost(),
'session_domain' => config('session.domain')
]);
}
return $next($request);
}
}

View file

@ -0,0 +1,61 @@
<?php
namespace App\Http\Middleware;
use App\Models\UserShop;
use App\Services\Util;
use Closure;
use Auth;
use Config;
use phpDocumentor\Reflection\DocBlock\Tags\Uses;
class Subdomain
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
// $tld = config('app.tld_care');
if(!empty($request->route('subdomain'))){
//sub.mivita.care
$user_shop = UserShop::where('slug', $request->route('subdomain'))->first();
$request->route()->forgetParameter('subdomain');
Util::setPostRoute('user/');
if($user_shop){
if(!$user_shop->active){
abort(503);
}
if(!$user_shop->user){
abort(503);
}
if(!$user_shop->user->isActiveShop()){
abort(503);
}
\Session::put('user_shop', $user_shop);
\Session::put('user_shop_domain', config('app.protocol').$user_shop->slug.".".config('app.domain').config('app.tld_care'));
Config::set('app.url', $user_shop->slug.".".config('app.domain').config('app.tld_care'));
return $next($request);
}
}else{
//mivita.shop
//$tld = config('app.tld_shop');
$user_shop = UserShop::where('slug', 'aloevera')->first();
//$request->route()->forgetParameter('subdomain');
Util::setPostRoute('user/');
if($user_shop){
\Session::put('user_shop', $user_shop);
\Session::put('user_shop_domain', config('app.protocol').config('app.domain').config('app.tld_shop'));
Config::set('app.url', config('app.domain').config('app.tld_shop'));
return $next($request);
}
}
return redirect(config('app.url'));
}
}

View file

@ -0,0 +1,300 @@
# Current Implementation Issues Analysis
## Executive Summary
The current multi-domain and subdomain implementation in the Mivita application has several architectural issues that impact maintainability, performance, and scalability. This document provides a detailed analysis of these issues and their implications.
## 1. Middleware Issues
### 1.1 Subdomain Middleware (`app/Http/Middleware/Subdomain.php`)
#### Critical Issues:
**Hard-coded Fallback Logic**
```php
// Line 47: Hard-coded shop selection
$user_shop = UserShop::where('slug', 'aloevera')->first();
```
- **Impact**: Inflexible fallback mechanism
- **Risk**: Cannot easily change default shop
- **Maintainability**: Low - requires code changes for configuration
**Mixed Responsibilities**
```php
// Lines 24-43: Dynamic subdomain handling
// Lines 44-57: Main domain handling
```
- **Issue**: Single middleware handles multiple domain types
- **Impact**: Complex conditional logic
- **Maintainability**: Difficult to test and modify
**Direct Session Manipulation**
```php
// Lines 39-41: Direct session writes
\Session::put('user_shop', $user_shop);
\Session::put('user_shop_domain', config('app.protocol').$user_shop->slug.".".config('app.domain').config('app.tld_care'));
Config::set('app.url', $user_shop->slug.".".config('app.domain').config('app.tld_care'));
```
- **Issue**: Middleware directly modifies global state
- **Risk**: Side effects and testing difficulties
- **Best Practice**: Middleware should be stateless
**No Error Handling**
- **Issue**: No validation of user shop status
- **Risk**: Invalid shops can cause 503 errors
- **Missing**: Graceful degradation
### 1.2 Missing Validation
**User Shop Validation Issues**:
```php
// Lines 30-38: Validation logic
if(!$user_shop->active){
abort(503);
}
if(!$user_shop->user){
abort(503);
}
if(!$user_shop->user->isActiveShop()){
abort(503);
}
```
- **Issue**: Returns 503 (Service Unavailable) for invalid shops
- **Better**: Should return 404 or redirect to main domain
- **SEO Impact**: 503 errors can negatively affect search rankings
## 2. Routing Architecture Issues
### 2.1 Route File Organization
Current structure:
```
routes/
├── web.php (mostly empty)
├── main.php
├── subdomain.php
├── crm.php
├── portal.php
├── checkout.php
├── api.php
└── utility.php
```
#### Issues:
**Route Duplication**
- Legal routes (`/datenschutz`, `/impressum`, `/agb`) duplicated across multiple files
- Contact routes duplicated
- Registration routes duplicated
**Inconsistent Middleware Application**
```php
// crm.php - Line 12: Domain-based grouping
Route::domain(config('app.pre_url_crm') . config('app.domain') . config('app.tld_care'))->group(function () {
// subdomain.php - Line 10: Middleware-based grouping
Route::group(['middleware' => ['subdomain']], function () {
```
**Complex Domain Logic in Routes**
- Domain configuration scattered across route files
- Hard to understand which routes belong to which domain
- Difficult to add new domain types
### 2.2 Route Registration Issues
**Missing Route Prefixes**
- No clear namespacing for different domain types
- Route name conflicts possible
- Difficult to generate domain-specific URLs
**Inefficient Route Loading**
- All routes loaded regardless of current domain
- Impacts performance for large applications
- Unnecessary route compilation
## 3. Configuration Management Issues
### 3.1 Environment Configuration (`.env`)
Current configuration:
```env
APP_DOMAIN=mivita
APP_TLD_CARE=.test
APP_TLD_SHOP=.lshop
APP_URL_CHECKOUT=checkout.
APP_URL_CRM=my.
APP_URL_PORTAL=in.
```
#### Issues:
**Inconsistent Naming**
- `APP_TLD_CARE` vs `APP_TLD_SHOP` - inconsistent naming pattern
- `APP_URL_*` contains trailing dots - configuration inconsistency
**Missing Validation**
- No validation of domain configuration
- Invalid configurations can cause runtime errors
- No documentation of required format
**Environment Dependency**
- Different TLDs for different environments
- Configuration changes required for different deployments
- No centralized domain management
### 3.2 Runtime Configuration Issues
**Dynamic URL Setting**
```php
// Subdomain.php - Line 41
Config::set('app.url', $user_shop->slug.".".config('app.domain').config('app.tld_care'));
```
- **Issue**: Runtime modification of application URL
- **Risk**: Affects URL generation throughout application
- **Problem**: Can cause inconsistent URLs in different parts of application
## 4. Performance Issues
### 4.1 Database Queries
**No Caching**
```php
// Line 26: Database query on every request
$user_shop = UserShop::where('slug', $request->route('subdomain'))->first();
```
- **Impact**: Database query for every subdomain request
- **Scale**: Significant load with many user shops
- **Solution**: Implement caching strategy
**N+1 Query Potential**
```php
// Lines 33-37: Potential additional queries
if(!$user_shop->user){
abort(503);
}
if(!$user_shop->user->isActiveShop()){
abort(503);
}
```
- **Issue**: Multiple database queries per request
- **Impact**: Poor performance with many concurrent requests
### 4.2 Route Compilation
**All Routes Loaded**
- Every request loads all route files
- No domain-specific route caching
- Impacts application bootstrap time
## 5. Security Issues
### 5.1 Session Management
**Inconsistent Session Domains**
```php
// .env - Line 26
SESSION_DOMAIN=.mivita.test
```
- **Issue**: Fixed session domain across all subdomains
- **Risk**: Session sharing between unrelated domains
- **Security**: Potential session hijacking between user shops
### 5.2 CSRF Protection
**Missing Domain-Specific CSRF**
- No domain-specific CSRF token handling
- Potential cross-domain CSRF issues
- Missing validation for domain-specific requests
## 6. Maintainability Issues
### 6.1 Code Organization
**Scattered Domain Logic**
- Domain handling logic in multiple files
- No single source of truth for domain configuration
- Difficult to understand complete domain architecture
**Missing Abstractions**
- No domain context object
- Direct use of request/session data
- Tight coupling between components
### 6.2 Testing Challenges
**Difficult to Test**
- Middleware has side effects
- Global state modifications
- Complex conditional logic
**Missing Test Coverage**
- No unit tests for domain logic
- Integration tests difficult to write
- Manual testing required for each domain type
## 7. Scalability Issues
### 7.1 Adding New Domains
**Hard to Extend**
- Adding new subdomain types requires multiple file changes
- No consistent pattern for new domain types
- Complex configuration requirements
### 7.2 Multi-tenant Considerations
**Poor Tenant Isolation**
- User shops not properly isolated
- Shared configuration between tenants
- Potential data leakage between shops
## 8. Documentation Issues
### 8.1 Missing Documentation
**No Architecture Documentation**
- Domain structure not documented
- Routing logic not explained
- Configuration options not documented
**No Deployment Guide**
- Missing deployment instructions
- No environment-specific guidance
- No troubleshooting documentation
## Impact Assessment
### High Impact Issues
1. **Performance**: Database queries on every request
2. **Security**: Session domain configuration issues
3. **Maintainability**: Scattered domain logic
### Medium Impact Issues
1. **Route duplication**: Maintenance overhead
2. **Configuration management**: Deployment complexity
3. **Error handling**: Poor user experience
### Low Impact Issues
1. **Code organization**: Developer productivity
2. **Documentation**: Onboarding difficulty
3. **Testing**: Quality assurance challenges
## Recommendations Priority
### Priority 1 (Critical)
1. Implement caching for user shop lookups
2. Fix session domain configuration
3. Improve error handling for invalid shops
### Priority 2 (High)
1. Refactor middleware architecture
2. Reorganize route structure
3. Centralize domain configuration
### Priority 3 (Medium)
1. Add comprehensive testing
2. Create documentation
3. Implement monitoring
This analysis provides the foundation for the optimization proposal detailed in the main README.md file.

View file

@ -0,0 +1,435 @@
# Implementation Guide - Mivita Subdomain Optimization
## Overview
This guide provides step-by-step instructions for implementing the optimized multi-domain and subdomain architecture for the Mivita application.
## Prerequisites
- Backup of current system
- Test environment available
- Access to web server configuration
- Understanding of Laravel routing and middleware
## Phase 1: Foundation Setup
### Step 1: Create New Service Classes
1. **Create DomainService**
```bash
# Copy from dev/subdomain-optimization/DomainService.php
cp dev/subdomain-optimization/DomainService.php app/Services/DomainService.php
```
2. **Create DomainContext**
```bash
# Create Domain directory and copy context
mkdir -p app/Domain
cp dev/subdomain-optimization/DomainContext.php app/Domain/DomainContext.php
```
3. **Create New Middleware**
```bash
# Copy enhanced middleware
cp dev/subdomain-optimization/DomainResolver.php app/Http/Middleware/DomainResolver.php
```
### Step 2: Register New Services
Add to `config/app.php`:
```php
'providers' => [
// ... existing providers
App\Providers\DomainServiceProvider::class,
],
```
Create `app/Providers/DomainServiceProvider.php`:
```php
<?php
namespace App\Providers;
use App\Services\DomainService;
use Illuminate\Support\ServiceProvider;
class DomainServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(DomainService::class);
}
public function boot()
{
//
}
}
```
### Step 3: Update Kernel Configuration
Add new middleware to `app/Http/Kernel.php`:
```php
protected $middleware = [
// ... existing middleware
\App\Http\Middleware\DomainResolver::class,
];
protected $middlewareGroups = [
'web' => [
// Remove old Subdomain middleware
// \App\Http\Middleware\Subdomain::class,
// ... other middleware
],
];
```
## Phase 2: Route Structure Migration
### Step 1: Create New Route Structure
```bash
# Create new directory structure
mkdir -p routes/domains/subdomains
mkdir -p routes/shared/crm
mkdir -p routes/shared/api
# Create placeholder files
touch routes/domains/main.php
touch routes/domains/shop.php
touch routes/domains/subdomains/crm.php
touch routes/domains/subdomains/portal.php
touch routes/domains/subdomains/checkout.php
touch routes/domains/subdomains/user-shops.php
touch routes/shared/legal.php
touch routes/shared/common.php
```
### Step 2: Migrate Existing Routes
1. **Legal Routes** (move to `routes/shared/legal.php`)
- Extract legal routes from all current route files
- Remove duplicates
- Standardize naming
2. **Main Domain Routes** (move to `routes/domains/main.php`)
- Extract routes from `routes/main.php`
- Clean up and organize
3. **CRM Routes** (move to `routes/domains/subdomains/crm.php`)
- Extract routes from `routes/crm.php`
- Split into sub-files for organization
4. **User Shop Routes** (move to `routes/domains/subdomains/user-shops.php`)
- Extract routes from `routes/subdomain.php`
- Remove domain-specific logic (handled by middleware)
### Step 3: Update Route Service Provider
Replace `app/Providers/RouteServiceProvider.php` with the optimized version:
```bash
cp dev/subdomain-optimization/RouteServiceProvider.php app/Providers/RouteServiceProvider.php
```
## Phase 3: Testing and Validation
### Step 1: Unit Tests
Create tests for new components:
```bash
php artisan make:test DomainServiceTest --unit
php artisan make:test DomainContextTest --unit
php artisan make:test DomainResolverTest
```
### Step 2: Feature Tests
Test each domain type:
```bash
php artisan make:test MainDomainTest
php artisan make:test CrmDomainTest
php artisan make:test UserShopDomainTest
php artisan make:test CheckoutDomainTest
php artisan make:test PortalDomainTest
```
### Step 3: Manual Testing
Test each domain type manually:
1. **Main Domain** (`mivita.care`)
- Homepage loads correctly
- Registration works
- Contact forms work
- Legal pages accessible
2. **CRM Domain** (`my.mivita.care`)
- Login system works
- User dashboard accessible
- Admin functions work
- Session management correct
3. **User Shops** (`{slug}.mivita.care`)
- Valid shops load correctly
- Invalid shops redirect properly
- Shopping cart functionality works
- Session isolation working
4. **Checkout Domain** (`checkout.mivita.care`)
- Payment processing works
- Transaction handling correct
- Security measures in place
5. **Portal Domain** (`in.mivita.care`)
- Customer login works
- Portal functionality accessible
- Data isolation correct
## Phase 4: Performance Optimization
### Step 1: Enable Caching
1. **Route Caching**
```bash
php artisan route:cache
```
2. **User Shop Caching**
- Configure Redis/Memcached
- Test cache invalidation
- Monitor cache hit rates
### Step 2: Database Optimization
1. **Add Indexes**
```sql
CREATE INDEX idx_user_shops_slug_active ON user_shops (slug, active);
CREATE INDEX idx_users_shop_active ON users (id) WHERE shop_active = 1;
```
2. **Query Optimization**
- Review N+1 queries
- Optimize user shop lookups
- Add eager loading where needed
### Step 3: Monitoring Setup
1. **Application Monitoring**
- Add performance metrics
- Monitor response times per domain
- Track error rates by domain type
2. **Cache Monitoring**
- Monitor cache hit/miss rates
- Track cache memory usage
- Set up cache warming
## Phase 5: Deployment Strategy
### Step 1: Feature Flags
Implement feature flags for gradual rollout:
```php
// In DomainResolver middleware
if (config('features.new_domain_resolver', false)) {
// Use new logic
} else {
// Fall back to old middleware
}
```
### Step 2: Blue-Green Deployment
1. **Prepare Blue Environment**
- Deploy new code to blue environment
- Test all functionality
- Verify performance metrics
2. **Switch Traffic**
- Gradually route traffic to blue
- Monitor for issues
- Ready to rollback if needed
### Step 3: Monitoring During Deployment
1. **Real-time Monitoring**
- Response times
- Error rates
- User shop accessibility
- Payment processing success
2. **Rollback Triggers**
- Error rate > 1%
- Response time > 2x baseline
- Payment failures > 0.1%
- User complaints
## Rollback Plan
### Immediate Rollback (< 5 minutes)
1. **Revert Middleware**
```php
// In Kernel.php, re-enable old middleware
\App\Http\Middleware\Subdomain::class,
```
2. **Revert Route Service Provider**
```bash
git checkout HEAD~1 app/Providers/RouteServiceProvider.php
```
3. **Clear Caches**
```bash
php artisan route:clear
php artisan config:clear
php artisan cache:clear
```
### Full Rollback (< 30 minutes)
1. **Revert All Changes**
```bash
git revert <commit-hash>
```
2. **Redeploy Previous Version**
```bash
# Deploy previous known-good version
```
3. **Verify Functionality**
- Test all domain types
- Verify user shop access
- Check payment processing
## Configuration Management
### Environment Variables
Add to `.env`:
```env
# Domain optimization feature flags
DOMAIN_RESOLVER_ENABLED=true
DOMAIN_CACHE_ENABLED=true
DOMAIN_CACHE_TTL=3600
# Monitoring
DOMAIN_MONITORING_ENABLED=true
DOMAIN_PERFORMANCE_TRACKING=true
```
### Configuration Files
Update `config/domains.php`:
```php
<?php
return [
'cache_ttl' => env('DOMAIN_CACHE_TTL', 3600),
'monitoring_enabled' => env('DOMAIN_MONITORING_ENABLED', false),
'performance_tracking' => env('DOMAIN_PERFORMANCE_TRACKING', false),
'fixed_subdomains' => ['my', 'in', 'checkout'],
'middleware_stacks' => [
'main' => ['web'],
'crm' => ['web', 'auth'],
'portal' => ['web', 'auth:customers'],
'checkout' => ['web', 'checkout'],
'user-shop' => ['web', 'subdomain'],
],
];
```
## Maintenance Procedures
### Regular Maintenance
1. **Weekly Cache Clearing**
```bash
# Clear expired user shop cache entries
php artisan domain:cache:clean
```
2. **Monthly Performance Review**
- Review response time metrics
- Check cache hit rates
- Analyze error patterns
3. **Quarterly Domain Audit**
- Review user shop validity
- Clean up inactive shops
- Update domain configurations
### Emergency Procedures
1. **User Shop Outage**
- Identify affected shops
- Clear specific cache entries
- Notify affected users
2. **Domain Resolution Issues**
- Check DNS configuration
- Verify middleware configuration
- Test domain parsing logic
3. **Performance Degradation**
- Check cache status
- Review database performance
- Scale resources if needed
## Success Metrics
### Performance Metrics
- **Response Time**: < 200ms for cached requests
- **Cache Hit Rate**: > 95% for user shop lookups
- **Error Rate**: < 0.1% across all domains
- **Uptime**: > 99.9% per domain type
### Business Metrics
- **User Shop Accessibility**: 100% for active shops
- **Payment Success Rate**: > 99.5%
- **Customer Satisfaction**: No complaints about domain issues
- **Development Velocity**: Faster feature development per domain
### Technical Metrics
- **Code Maintainability**: Reduced cyclomatic complexity
- **Test Coverage**: > 90% for domain-related code
- **Documentation**: All new components documented
- **Security**: No domain-based security vulnerabilities

View file

@ -0,0 +1,321 @@
# Optimized Route Structure
## Current Route Organization Issues
The current routing system has several problems:
- Route duplication across multiple files
- Complex domain-based routing spread across different files
- Inconsistent middleware application
- Hard to maintain and understand
## Proposed New Structure
```
routes/
├── web.php # Main route orchestrator
├── api.php # API routes (unchanged)
├── console.php # Console routes (unchanged)
├── channels.php # Broadcasting routes (unchanged)
├── domains/
│ ├── main.php # Main domain routes (mivita.care)
│ ├── shop.php # Shop domain routes (mivita.shop)
│ └── subdomains/
│ ├── crm.php # CRM routes (my.mivita.care)
│ ├── portal.php # Portal routes (in.mivita.care)
│ ├── checkout.php # Checkout routes (checkout.mivita.care)
│ └── user-shops.php # User shop routes ({slug}.mivita.care)
└── shared/
├── legal.php # Legal pages (shared across domains)
├── common.php # Common functionality
└── api/
└── v1.php # API version 1 routes
```
## Implementation Examples
### routes/web.php (Orchestrator)
```php
<?php
use App\Domain\DomainContext;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Web Routes Orchestrator
|--------------------------------------------------------------------------
|
| This file serves as the main orchestrator for all web routes.
| Domain-specific routes are loaded based on the current domain context.
|
*/
// Get domain context from middleware
$domainContext = app('domain.context');
// Load shared routes first (legal pages, etc.)
require __DIR__ . '/shared/legal.php';
require __DIR__ . '/shared/common.php';
// Load domain-specific routes based on context
match($domainContext->type) {
'main' => require __DIR__ . '/domains/main.php',
'main-shop' => require __DIR__ . '/domains/shop.php',
'crm' => require __DIR__ . '/domains/subdomains/crm.php',
'portal' => require __DIR__ . '/domains/subdomains/portal.php',
'checkout' => require __DIR__ . '/domains/subdomains/checkout.php',
'user-shop' => require __DIR__ . '/domains/subdomains/user-shops.php',
default => null // Unknown domains handled by middleware
};
```
### routes/domains/main.php (Main Domain)
```php
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Main Domain Routes (mivita.care)
|--------------------------------------------------------------------------
*/
Route::middleware(['domain.resolver'])->group(function () {
// Home page
Route::get('/', 'Web\SiteController@index')->name('home');
// Registration
Route::get('/registrierung', 'Web\RegisterController@index')->name('register_user');
Route::get('/reg/{member_id?}', 'Web\RegisterController@member')->name('register_user_member');
Route::post('/registrierung', 'Web\RegisterController@register')->name('register_user');
Route::get('/registrierung/finish', 'Web\RegisterController@finish')->name('register_user_finish');
// Contact
Route::get('/kontakt', 'Web\ContactController@create')->name('contact_create');
Route::post('/kontakt', 'Web\ContactController@store')->name('contact_store');
// Dynamic site routing
Route::get('/{site}/{subsite?}/{product_slug?}', 'Web\SiteController@site')->name('base.site');
// Language switching
Route::post('/change_website_lang', 'Web\SiteController@changeLang')->name('change_website_lang');
});
```
### routes/domains/subdomains/crm.php (CRM Domain)
```php
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| CRM Domain Routes (my.mivita.care)
|--------------------------------------------------------------------------
*/
Route::domain(config('app.pre_url_crm') . config('app.domain') . config('app.tld_care'))
->middleware(['domain.resolver', 'web'])
->group(function () {
// Cron jobs (public access)
Route::get('/cron/jobs/action/{action}/{key}', 'CronController@action')->name('cron_jobs_action');
Route::get('/cron/jobs/run/{key}', 'CronController@runCron')->name('cron_jobs_run');
// Authentication routes
Auth::routes();
Route::get('/logout', function () {
Auth::logout();
return redirect()->route('login');
})->name('logout');
// Public routes
Route::get('/', 'HomeController@index')->name('home');
Route::get('/register/verify/{confirmationCode}', 'HomeController@verify')->name('register_verify');
Route::get('/homeparty/{token?}/{gid?}', 'Web\HomepartyController@detail')->name('homeparty');
Route::post('/homeparty/{token?}/{gid?}', 'Web\HomepartyController@detailStore')->name('homeparty');
// Authenticated routes
Route::middleware(['auth'])->group(function () {
require __DIR__ . '/../../shared/crm/authenticated.php';
});
// Admin routes
Route::middleware(['admin'])->group(function () {
require __DIR__ . '/../../shared/crm/admin.php';
});
// Super admin routes
Route::middleware(['superadmin'])->group(function () {
require __DIR__ . '/../../shared/crm/superadmin.php';
});
// System admin routes
Route::middleware(['sysadmin'])->group(function () {
require __DIR__ . '/../../shared/crm/sysadmin.php';
});
});
```
### routes/domains/subdomains/user-shops.php (User Shops)
```php
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| User Shop Domain Routes ({slug}.mivita.care)
|--------------------------------------------------------------------------
*/
Route::middleware(['domain.resolver', 'web'])->group(function () {
// Home page
Route::get('/', 'Web\SiteController@index')->name('shop.home');
// Shopping cart functionality
Route::prefix('user/card')->name('user.card_')->group(function () {
Route::get('/add/{id}/{quantity?}/{product_slug?}', 'Web\CardController@addToCardGet')->name('add_get');
Route::post('/add/{id}', 'Web\CardController@addToCardPost')->name('add_post');
Route::get('/show', 'Web\CardController@showCard')->name('show');
Route::get('/checkout/server', 'Web\CardController@checkoutServer')->name('checkout_server');
Route::post('/update', 'Web\CardController@updateCard')->name('update');
Route::get('/remove/{rowId}', 'Web\CardController@removeCard')->name('remove');
Route::get('/delete', 'Web\CardController@deleteCard')->name('delete');
});
// Shop navigation
Route::get('/user/back/to/shop/{reference?}', 'Web\CardController@backToShop')->name('user.back_to_shop');
Route::get('/domain/check', 'Web\SiteController@domainCheck')->name('user.domain_check');
// Registration with referral
Route::get('/registrierung', 'Web\RegisterController@index')->name('shop.register_user');
Route::get('/reg/{member_id?}', 'Web\RegisterController@member')->name('shop.register_user_member');
Route::post('/registrierung', 'Web\RegisterController@register')->name('shop.register_user');
Route::get('/registrierung/finish', 'Web\RegisterController@finish')->name('shop.register_user_finish');
// Dynamic site routing (must be last)
Route::get('/{site}/{subsite?}/{product_slug?}', 'Web\SiteController@site')->name('shop.site');
// Language switching
Route::post('/change_website_lang', 'Web\SiteController@changeLang')->name('shop.change_website_lang');
});
```
### routes/shared/legal.php (Shared Legal Pages)
```php
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Shared Legal Routes
|--------------------------------------------------------------------------
| These routes are available across all domain types
*/
Route::get('/datenschutz', 'HomeController@legalDataProtected')->name('datenschutz');
Route::get('/impressum', 'HomeController@legalImprint')->name('impressum');
Route::get('/agb', 'HomeController@legalAGB')->name('agb');
// English routes
Route::get('/data-protection', 'HomeController@legalDataProtected')->name('data_protected');
Route::get('/imprint', 'HomeController@legalImprint')->name('imprint');
Route::get('/terms', 'HomeController@legalAGB')->name('terms');
```
### routes/shared/common.php (Common Functionality)
```php
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Common Routes
|--------------------------------------------------------------------------
| These routes provide common functionality across domains
*/
// Modal loading (AJAX)
Route::post('/loading/modal', 'HomeController@loadingModal')->name('loading_modal');
// Health check
Route::get('/health', function () {
return response()->json(['status' => 'ok']);
})->name('health_check');
// Maintenance mode check
Route::get('/maintenance', function () {
return view('maintenance');
})->name('maintenance');
```
## Benefits of New Structure
### 1. Clear Separation of Concerns
- Each domain type has its own route file
- Shared functionality is clearly separated
- Easy to understand which routes belong to which domain
### 2. Reduced Duplication
- Legal pages defined once and shared
- Common functionality centralized
- Domain-specific routes only defined once
### 3. Better Maintainability
- Changes to specific domain types are isolated
- Easier to add new domain types
- Clear structure for new developers
### 4. Performance Benefits
- Only relevant routes are loaded per domain
- Reduced route compilation time
- Better caching possibilities
### 5. Enhanced Security
- Domain-specific middleware applied correctly
- Easier to implement domain-specific security rules
- Clear boundaries between different application areas
## Migration Strategy
### Phase 1: Create New Structure
1. Create new directory structure
2. Copy existing routes to appropriate new files
3. Test each domain type individually
### Phase 2: Update Route Service Provider
1. Modify RouteServiceProvider to use new orchestrator
2. Implement domain context checking
3. Add fallback mechanisms
### Phase 3: Clean Up
1. Remove old route files
2. Update any hardcoded route references
3. Update documentation
### Phase 4: Optimize
1. Implement route caching per domain
2. Add performance monitoring
3. Optimize middleware stack per domain type