update 20.10.2025
This commit is contained in:
parent
8c11130b5d
commit
a939cd51ef
616 changed files with 84821 additions and 4121 deletions
73
dev/routes-optimization/AuthSessionDebugger.php
Normal file
73
dev/routes-optimization/AuthSessionDebugger.php
Normal 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;
|
||||
}
|
||||
}
|
||||
290
dev/routes-optimization/DomainContext.php
Normal file
290
dev/routes-optimization/DomainContext.php
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
||||
206
dev/routes-optimization/DomainResolver.php
Normal file
206
dev/routes-optimization/DomainResolver.php
Normal 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;
|
||||
}
|
||||
}
|
||||
284
dev/routes-optimization/DomainResolverBak.php
Normal file
284
dev/routes-optimization/DomainResolverBak.php
Normal 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;
|
||||
}
|
||||
}
|
||||
86
dev/routes-optimization/DomainServiceProvider.php
Normal file
86
dev/routes-optimization/DomainServiceProvider.php
Normal 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);
|
||||
}
|
||||
}
|
||||
233
dev/routes-optimization/README.md
Normal file
233
dev/routes-optimization/README.md
Normal 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.
|
||||
63
dev/routes-optimization/RouteCleanup.php
Normal file
63
dev/routes-optimization/RouteCleanup.php
Normal 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;
|
||||
}
|
||||
}
|
||||
344
dev/routes-optimization/RouteServiceProvider.php
Normal file
344
dev/routes-optimization/RouteServiceProvider.php
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
69
dev/routes-optimization/SessionDebugger.php
Normal file
69
dev/routes-optimization/SessionDebugger.php
Normal 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);
|
||||
}
|
||||
}
|
||||
61
dev/routes-optimization/Subdomain.php
Normal file
61
dev/routes-optimization/Subdomain.php
Normal 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'));
|
||||
|
||||
}
|
||||
}
|
||||
300
dev/routes-optimization/current-issues-analysis.md
Normal file
300
dev/routes-optimization/current-issues-analysis.md
Normal 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.
|
||||
435
dev/routes-optimization/implementation-guide.md
Normal file
435
dev/routes-optimization/implementation-guide.md
Normal 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
|
||||
321
dev/routes-optimization/optimized-routes-structure.md
Normal file
321
dev/routes-optimization/optimized-routes-structure.md
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue