commit 08-2025
This commit is contained in:
parent
9ae662f63e
commit
480fdc65ed
404 changed files with 65310 additions and 2600431 deletions
290
dev/subdomain-optimization/DomainContext.php
Normal file
290
dev/subdomain-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
|
||||
);
|
||||
}
|
||||
}
|
||||
261
dev/subdomain-optimization/DomainResolver.php
Normal file
261
dev/subdomain-optimization/DomainResolver.php
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Domain\DomainContext;
|
||||
use App\Services\DomainService;
|
||||
use App\Services\Util;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
/**
|
||||
* Enhanced Domain Resolver Middleware
|
||||
*
|
||||
* This middleware replaces the current Subdomain middleware with a more
|
||||
* robust, maintainable solution that properly handles all domain types.
|
||||
*/
|
||||
class DomainResolver
|
||||
{
|
||||
private DomainService $domainService;
|
||||
|
||||
public function __construct(DomainService $domainService)
|
||||
{
|
||||
$this->domainService = $domainService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
// Parse the domain from the request
|
||||
$host = $request->getHost();
|
||||
$domainInfo = $this->domainService->parseDomain($host);
|
||||
|
||||
// Create domain context
|
||||
$context = $this->createDomainContext($request, $domainInfo);
|
||||
|
||||
// Handle unknown domains
|
||||
if ($context->isUnknownDomain()) {
|
||||
return $this->handleUnknownDomain($request, $context);
|
||||
}
|
||||
|
||||
// Set up the application context
|
||||
$this->setupApplicationContext($request, $context);
|
||||
|
||||
// Store context in the application container
|
||||
app()->instance('domain.context', $context);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create domain context from parsed domain information
|
||||
*/
|
||||
private function createDomainContext(Request $request, array $domainInfo): DomainContext
|
||||
{
|
||||
$userShop = null;
|
||||
|
||||
// Load user shop for user-shop domains
|
||||
if ($domainInfo['type'] === 'user-shop' && $domainInfo['subdomain']) {
|
||||
$userShop = $this->domainService->getUserShop($domainInfo['subdomain']);
|
||||
|
||||
// If user shop is invalid, mark as unknown
|
||||
if (!$userShop) {
|
||||
$domainInfo['type'] = 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle main-shop domain with default shop
|
||||
if ($domainInfo['type'] === 'main-shop') {
|
||||
$userShop = $this->domainService->getDefaultUserShop();
|
||||
}
|
||||
|
||||
return DomainContext::fromDomainInfo($domainInfo, $userShop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle unknown or invalid domains
|
||||
*/
|
||||
private function handleUnknownDomain(Request $request, DomainContext $context)
|
||||
{
|
||||
// Log the invalid domain attempt
|
||||
\Log::warning('Unknown domain accessed', [
|
||||
'host' => $request->getHost(),
|
||||
'domain_info' => $context->toArray(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
'ip' => $request->ip()
|
||||
]);
|
||||
|
||||
// Redirect to main domain
|
||||
$mainUrl = $this->domainService->buildUrl('main');
|
||||
return redirect($mainUrl, 301);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up application context based on domain
|
||||
*/
|
||||
private function setupApplicationContext(Request $request, DomainContext $context): void
|
||||
{
|
||||
switch ($context->type) {
|
||||
case 'user-shop':
|
||||
$this->setupUserShopContext($request, $context);
|
||||
break;
|
||||
|
||||
case 'main-shop':
|
||||
$this->setupMainShopContext($request, $context);
|
||||
break;
|
||||
|
||||
case 'crm':
|
||||
$this->setupCrmContext($request, $context);
|
||||
break;
|
||||
|
||||
case 'portal':
|
||||
$this->setupPortalContext($request, $context);
|
||||
break;
|
||||
|
||||
case 'checkout':
|
||||
$this->setupCheckoutContext($request, $context);
|
||||
break;
|
||||
|
||||
case 'main':
|
||||
$this->setupMainContext($request, $context);
|
||||
break;
|
||||
}
|
||||
|
||||
// Set up common configurations
|
||||
$this->setupCommonContext($request, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up context for user shop domains
|
||||
*/
|
||||
private function setupUserShopContext(Request $request, DomainContext $context): void
|
||||
{
|
||||
if (!$context->userShop) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up session data (maintaining compatibility with current implementation)
|
||||
Session::put('user_shop', $context->userShop);
|
||||
Session::put('user_shop_domain', $context->fullDomain);
|
||||
|
||||
// Set dynamic URL configuration
|
||||
Config::set('app.url', $context->fullDomain);
|
||||
|
||||
// Set route prefix for utilities (maintaining compatibility)
|
||||
Util::setPostRoute('user/');
|
||||
|
||||
// Remove subdomain parameter from route (maintaining compatibility)
|
||||
if ($request->route() && $request->route()->hasParameter('subdomain')) {
|
||||
$request->route()->forgetParameter('subdomain');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up context for main shop domain (mivita.shop)
|
||||
*/
|
||||
private function setupMainShopContext(Request $request, DomainContext $context): void
|
||||
{
|
||||
if (!$context->userShop) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up session data similar to user shops
|
||||
Session::put('user_shop', $context->userShop);
|
||||
Session::put('user_shop_domain', $context->fullDomain);
|
||||
|
||||
// Set dynamic URL configuration
|
||||
Config::set('app.url', $context->fullDomain);
|
||||
|
||||
// Set route prefix
|
||||
Util::setPostRoute('user/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up context for CRM domain (my.mivita.care)
|
||||
*/
|
||||
private function setupCrmContext(Request $request, DomainContext $context): void
|
||||
{
|
||||
// Set up CRM-specific configurations
|
||||
Config::set('app.url', $context->fullDomain);
|
||||
|
||||
// Set session domain for cross-subdomain compatibility
|
||||
Config::set('session.domain', '.' . $context->domain . $context->tld);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up context for portal domain (in.mivita.care)
|
||||
*/
|
||||
private function setupPortalContext(Request $request, DomainContext $context): void
|
||||
{
|
||||
// Set up portal-specific configurations
|
||||
Config::set('app.url', $context->fullDomain);
|
||||
|
||||
// Set session domain for cross-subdomain compatibility
|
||||
Config::set('session.domain', '.' . $context->domain . $context->tld);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up context for checkout domain (checkout.mivita.care)
|
||||
*/
|
||||
private function setupCheckoutContext(Request $request, DomainContext $context): void
|
||||
{
|
||||
// Set up checkout-specific configurations
|
||||
Config::set('app.url', $context->fullDomain);
|
||||
|
||||
// Set session domain for cross-subdomain compatibility
|
||||
Config::set('session.domain', '.' . $context->domain . $context->tld);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up context for main domain (mivita.care)
|
||||
*/
|
||||
private function setupMainContext(Request $request, DomainContext $context): void
|
||||
{
|
||||
// Set up main domain configurations
|
||||
Config::set('app.url', $context->fullDomain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up common context configurations
|
||||
*/
|
||||
private function setupCommonContext(Request $request, DomainContext $context): void
|
||||
{
|
||||
// Set up view namespace if applicable
|
||||
if ($namespace = $context->getViewNamespace()) {
|
||||
// This could be used by view composers or other components
|
||||
Config::set('view.domain_namespace', $namespace);
|
||||
}
|
||||
|
||||
// Set up any domain-specific cache prefixes
|
||||
if ($context->isUserShopDomain()) {
|
||||
Config::set('cache.prefix', config('cache.prefix') . '_' . $context->getUserShopSlug());
|
||||
}
|
||||
|
||||
// Set up CSRF token domain
|
||||
if (in_array($context->type, ['crm', 'portal', 'checkout'])) {
|
||||
Config::set('session.same_site', 'lax');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the domain is properly configured for this request
|
||||
*/
|
||||
private function validateDomainConfiguration(DomainContext $context): bool
|
||||
{
|
||||
$errors = $this->domainService->validateConfiguration();
|
||||
|
||||
if (!empty($errors)) {
|
||||
\Log::error('Domain configuration errors', [
|
||||
'errors' => $errors,
|
||||
'context' => $context->toArray()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
272
dev/subdomain-optimization/DomainService.php
Normal file
272
dev/subdomain-optimization/DomainService.php
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\UserShop;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Domain Service - Centralized domain and subdomain management
|
||||
*
|
||||
* This service provides a centralized way to handle domain resolution,
|
||||
* subdomain validation, and domain-specific configuration management.
|
||||
*/
|
||||
class DomainService
|
||||
{
|
||||
private const CACHE_TTL = 3600; // 1 hour
|
||||
private const FIXED_SUBDOMAINS = ['my', 'in', 'checkout'];
|
||||
|
||||
private array $domainConfig;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->domainConfig = $this->loadDomainConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load domain configuration from config files
|
||||
*/
|
||||
private function loadDomainConfiguration(): array
|
||||
{
|
||||
return [
|
||||
'main_domain' => config('app.domain'),
|
||||
'main_tld' => config('app.tld_care'),
|
||||
'shop_tld' => config('app.tld_shop'),
|
||||
'protocol' => config('app.protocol'),
|
||||
'subdomains' => [
|
||||
'crm' => config('app.pre_url_crm', 'my.'),
|
||||
'portal' => config('app.pre_url_portal', 'in.'),
|
||||
'checkout' => config('app.pre_url_checkout', 'checkout.'),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the type of subdomain
|
||||
*/
|
||||
public function getSubdomainType(string $subdomain): string
|
||||
{
|
||||
// Check if it's a fixed subdomain
|
||||
if (in_array($subdomain, self::FIXED_SUBDOMAINS)) {
|
||||
return match($subdomain) {
|
||||
'my' => 'crm',
|
||||
'in' => 'portal',
|
||||
'checkout' => 'checkout',
|
||||
default => 'unknown'
|
||||
};
|
||||
}
|
||||
|
||||
// Check if it's a valid user shop
|
||||
if ($this->isValidUserShop($subdomain)) {
|
||||
return 'user-shop';
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a subdomain represents a valid user shop
|
||||
*/
|
||||
public function isValidUserShop(string $slug): bool
|
||||
{
|
||||
$cacheKey = "user_shop_valid_{$slug}";
|
||||
|
||||
return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($slug) {
|
||||
$userShop = UserShop::where('slug', $slug)->first();
|
||||
|
||||
if (!$userShop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$userShop->active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$userShop->user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$userShop->user->isActiveShop()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user shop by slug with caching
|
||||
*/
|
||||
public function getUserShop(string $slug): ?UserShop
|
||||
{
|
||||
if (!$this->isValidUserShop($slug)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cacheKey = "user_shop_{$slug}";
|
||||
|
||||
return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($slug) {
|
||||
return UserShop::where('slug', $slug)
|
||||
->with('user')
|
||||
->first();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse domain from request and determine context
|
||||
*/
|
||||
public function parseDomain(string $host): array
|
||||
{
|
||||
$parts = explode('.', $host);
|
||||
|
||||
// Handle different TLD scenarios
|
||||
if (count($parts) < 2) {
|
||||
return [
|
||||
'type' => 'invalid',
|
||||
'domain' => $host,
|
||||
'subdomain' => null,
|
||||
'tld' => null
|
||||
];
|
||||
}
|
||||
|
||||
// Extract TLD and domain
|
||||
$tld = '.' . end($parts);
|
||||
$domain = $parts[count($parts) - 2];
|
||||
|
||||
// Check for subdomain
|
||||
$subdomain = null;
|
||||
if (count($parts) > 2) {
|
||||
$subdomain = $parts[0];
|
||||
}
|
||||
|
||||
// Determine domain type
|
||||
$type = $this->determineDomainType($domain, $subdomain, $tld);
|
||||
|
||||
return [
|
||||
'type' => $type,
|
||||
'domain' => $domain,
|
||||
'subdomain' => $subdomain,
|
||||
'tld' => $tld,
|
||||
'full_domain' => $host
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine domain type based on domain, subdomain, and TLD
|
||||
*/
|
||||
private function determineDomainType(string $domain, ?string $subdomain, string $tld): string
|
||||
{
|
||||
// Check if it's the main domain
|
||||
if ($domain === $this->domainConfig['main_domain']) {
|
||||
if ($subdomain === null) {
|
||||
// Main domain without subdomain
|
||||
if ($tld === $this->domainConfig['shop_tld']) {
|
||||
return 'main-shop'; // mivita.shop
|
||||
}
|
||||
return 'main'; // mivita.care
|
||||
}
|
||||
|
||||
// Main domain with subdomain
|
||||
return $this->getSubdomainType($subdomain);
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Build URL for specific domain type
|
||||
*/
|
||||
public function buildUrl(string $type, ?string $path = null, ?string $slug = null): string
|
||||
{
|
||||
$base = $this->domainConfig['protocol'];
|
||||
|
||||
switch ($type) {
|
||||
case 'main':
|
||||
$base .= $this->domainConfig['main_domain'] . $this->domainConfig['main_tld'];
|
||||
break;
|
||||
|
||||
case 'main-shop':
|
||||
$base .= $this->domainConfig['main_domain'] . $this->domainConfig['shop_tld'];
|
||||
break;
|
||||
|
||||
case 'crm':
|
||||
$base .= rtrim($this->domainConfig['subdomains']['crm'], '.') .
|
||||
$this->domainConfig['main_domain'] . $this->domainConfig['main_tld'];
|
||||
break;
|
||||
|
||||
case 'portal':
|
||||
$base .= rtrim($this->domainConfig['subdomains']['portal'], '.') .
|
||||
$this->domainConfig['main_domain'] . $this->domainConfig['main_tld'];
|
||||
break;
|
||||
|
||||
case 'checkout':
|
||||
$base .= rtrim($this->domainConfig['subdomains']['checkout'], '.') .
|
||||
$this->domainConfig['main_domain'] . $this->domainConfig['main_tld'];
|
||||
break;
|
||||
|
||||
case 'user-shop':
|
||||
if (!$slug) {
|
||||
throw new \InvalidArgumentException('Slug required for user-shop URLs');
|
||||
}
|
||||
$base .= $slug . '.' . $this->domainConfig['main_domain'] . $this->domainConfig['main_tld'];
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \InvalidArgumentException("Unknown domain type: {$type}");
|
||||
}
|
||||
|
||||
if ($path) {
|
||||
$base .= '/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
return $base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get domain configuration
|
||||
*/
|
||||
public function getDomainConfiguration(): array
|
||||
{
|
||||
return $this->domainConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear user shop cache
|
||||
*/
|
||||
public function clearUserShopCache(string $slug): void
|
||||
{
|
||||
Cache::forget("user_shop_valid_{$slug}");
|
||||
Cache::forget("user_shop_{$slug}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default user shop for main domain (fallback)
|
||||
*/
|
||||
public function getDefaultUserShop(): ?UserShop
|
||||
{
|
||||
return $this->getUserShop('aloevera'); // Current hardcoded fallback
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate domain configuration
|
||||
*/
|
||||
public function validateConfiguration(): array
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
if (empty($this->domainConfig['main_domain'])) {
|
||||
$errors[] = 'Main domain not configured';
|
||||
}
|
||||
|
||||
if (empty($this->domainConfig['main_tld'])) {
|
||||
$errors[] = 'Main TLD not configured';
|
||||
}
|
||||
|
||||
if (empty($this->domainConfig['protocol'])) {
|
||||
$errors[] = 'Protocol not configured';
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
233
dev/subdomain-optimization/README.md
Normal file
233
dev/subdomain-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.
|
||||
344
dev/subdomain-optimization/RouteServiceProvider.php
Normal file
344
dev/subdomain-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);
|
||||
});
|
||||
}
|
||||
}
|
||||
300
dev/subdomain-optimization/current-issues-analysis.md
Normal file
300
dev/subdomain-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/subdomain-optimization/implementation-guide.md
Normal file
435
dev/subdomain-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/subdomain-optimization/optimized-routes-structure.md
Normal file
321
dev/subdomain-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