update 20.10.2025
This commit is contained in:
parent
8c11130b5d
commit
a939cd51ef
616 changed files with 84821 additions and 4121 deletions
|
|
@ -0,0 +1,248 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Models\UserShop;
|
||||
use App\Domain\EarlyDomainParser;
|
||||
use App\Services\Util;
|
||||
use Closure;
|
||||
use Config;
|
||||
use Session;
|
||||
|
||||
/**
|
||||
* Lightweight subdomain resolution middleware
|
||||
*
|
||||
* Uses config/domains.php for domain configuration and provides
|
||||
* simple, working subdomain handling without session timing issues.
|
||||
*/
|
||||
class SubdomainResolver
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
// Skip for API and asset requests
|
||||
if (!$this->shouldProcess($request)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
// Parse domain information using EarlyDomainParser
|
||||
$host = $request->getHost();
|
||||
$domainInfo = EarlyDomainParser::parseDomain($host);
|
||||
|
||||
// Route to appropriate handler based on domain type
|
||||
return match ($domainInfo['type']) {
|
||||
'user-shop' => $this->handleUserShop($request, $next, $domainInfo),
|
||||
'main-shop' => $this->handleMainShop($request, $next, $domainInfo),
|
||||
'main' => $this->handleMainCare($request, $next, $domainInfo),
|
||||
'crm' => $this->handleCrm($request, $next, $domainInfo),
|
||||
'portal' => $this->handlePortal($request, $next, $domainInfo),
|
||||
'checkout' => $this->handleCheckout($request, $next, $domainInfo),
|
||||
default => $this->handleUnknownDomain($request, $domainInfo),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle user shop subdomain (e.g., user.mivita.care)
|
||||
*/
|
||||
private function handleUserShop($request, Closure $next, array $domainInfo)
|
||||
{
|
||||
$subdomain = $domainInfo['subdomain'];
|
||||
$userShop = UserShop::where('slug', $subdomain)->first();
|
||||
|
||||
// Remove subdomain parameter from route
|
||||
if ($request->route('subdomain')) {
|
||||
$request->route()->forgetParameter('subdomain');
|
||||
}
|
||||
|
||||
if (!$userShop) {
|
||||
return $this->handleUnknownDomain($request, $domainInfo);
|
||||
}
|
||||
|
||||
// Validate shop status
|
||||
if (!$userShop->active || !$userShop->user || !$userShop->user->isActiveShop()) {
|
||||
abort(503, 'Shop temporarily unavailable');
|
||||
}
|
||||
|
||||
// Configure session domain based on domain config
|
||||
$this->configureSessionDomain($domainInfo['host']);
|
||||
|
||||
// Set up application context
|
||||
$this->setupUserShopContext($userShop, $subdomain, $domainInfo['host']);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle main shop domain (e.g., mivita.shop)
|
||||
*/
|
||||
private function handleMainShop($request, Closure $next, array $domainInfo)
|
||||
{
|
||||
// Load default shop from domain config
|
||||
$defaultShop = $this->getDefaultShopFromConfig('shop');
|
||||
$userShop = UserShop::where('slug', $defaultShop)->first();
|
||||
|
||||
// Configure session domain based on domain config
|
||||
$this->configureSessionDomain($domainInfo['host']);
|
||||
|
||||
if ($userShop) {
|
||||
$this->setupUserShopContext($userShop, null, $domainInfo['host']);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle main care domain (e.g., mivita.care)
|
||||
*/
|
||||
private function handleMainCare($request, Closure $next, array $domainInfo)
|
||||
{
|
||||
// Configure session domain based on domain config
|
||||
$this->configureSessionDomain($domainInfo['host']);
|
||||
|
||||
// Clear any existing shop session data
|
||||
Session::forget('user_shop');
|
||||
Session::forget('user_shop_domain');
|
||||
|
||||
// Set app URL
|
||||
Config::set('app.url', EarlyDomainParser::getProtocol() . $domainInfo['host']);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle CRM domain (e.g., my.mivita.care)
|
||||
*/
|
||||
private function handleCrm($request, Closure $next, array $domainInfo)
|
||||
{
|
||||
// Configure session domain for CRM
|
||||
$this->configureSessionDomain($domainInfo['host']);
|
||||
|
||||
// Clear shop data for CRM
|
||||
Session::forget('user_shop');
|
||||
Session::forget('user_shop_domain');
|
||||
|
||||
// Set app URL
|
||||
Config::set('app.url', EarlyDomainParser::getProtocol() . $domainInfo['host']);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Portal domain (e.g., in.mivita.care)
|
||||
*/
|
||||
private function handlePortal($request, Closure $next, array $domainInfo)
|
||||
{
|
||||
// Configure session domain for Portal
|
||||
$this->configureSessionDomain($domainInfo['host']);
|
||||
|
||||
// Clear shop data for Portal
|
||||
Session::forget('user_shop');
|
||||
Session::forget('user_shop_domain');
|
||||
|
||||
// Set app URL
|
||||
Config::set('app.url', EarlyDomainParser::getProtocol() . $domainInfo['host']);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Checkout domain (e.g., checkout.mivita.care)
|
||||
*/
|
||||
private function handleCheckout($request, Closure $next, array $domainInfo)
|
||||
{
|
||||
// Configure session domain for Checkout
|
||||
$this->configureSessionDomain($domainInfo['host']);
|
||||
|
||||
// Keep existing shop session data for checkout
|
||||
// Don't clear user_shop - checkout needs to know which shop
|
||||
|
||||
// Set app URL
|
||||
Config::set('app.url', EarlyDomainParser::getProtocol() . $domainInfo['host']);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle unknown domains
|
||||
*/
|
||||
private function handleUnknownDomain($request, array $domainInfo)
|
||||
{
|
||||
// Redirect to main domain using EarlyDomainParser
|
||||
$mainUrl = EarlyDomainParser::getMainUrl();
|
||||
|
||||
return redirect()->away($mainUrl, 301);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up user shop context in session and config
|
||||
*/
|
||||
private function setupUserShopContext(UserShop $userShop, ?string $subdomain = null, string $host = '')
|
||||
{
|
||||
// Put shop data in session
|
||||
Session::put('user_shop', $userShop);
|
||||
|
||||
// Build shop domain URL using EarlyDomainParser
|
||||
$shopDomain = EarlyDomainParser::getProtocol() . $host;
|
||||
Session::put('user_shop_domain', $shopDomain);
|
||||
|
||||
// Set app URL for URL generation
|
||||
Config::set('app.url', rtrim($shopDomain, '/'));
|
||||
|
||||
// Set post route for compatibility
|
||||
Util::setPostRoute('user/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure session domain based on host
|
||||
*/
|
||||
private function configureSessionDomain(string $host): void
|
||||
{
|
||||
// Extract TLD from host to determine session domain
|
||||
if (str_contains($host, config('app.tld_care'))) {
|
||||
Config::set('session.domain', '.' . config('app.domain') . config('app.tld_care'));
|
||||
} elseif (str_contains($host, config('app.tld_shop'))) {
|
||||
Config::set('session.domain', '.' . config('app.domain') . config('app.tld_shop'));
|
||||
} else {
|
||||
// Fallback to care domain
|
||||
Config::set('session.domain', '.' . config('app.domain') . config('app.tld_care'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default shop slug from domain configuration
|
||||
*/
|
||||
private function getDefaultShopFromConfig(string $domainKey): string
|
||||
{
|
||||
return config("domains.domains.{$domainKey}.default_user_shop", 'aloevera');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if request should be processed by this middleware
|
||||
*/
|
||||
private function shouldProcess($request): bool
|
||||
{
|
||||
// Skip API requests
|
||||
if ($request->is('api/*')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip asset requests
|
||||
if ($request->isMethod('GET') && preg_match('/\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$/i', $request->path())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip Laravel internal requests
|
||||
if ($request->is('_debugbar/*')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,247 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Models\UserShop;
|
||||
use App\Domain\EarlyDomainParser;
|
||||
use App\Services\Util;
|
||||
use Closure;
|
||||
use Config;
|
||||
use Session;
|
||||
|
||||
/**
|
||||
* Lightweight subdomain resolution middleware
|
||||
*
|
||||
* Uses EarlyDomainParser for domain configuration and provides
|
||||
* simple, working subdomain handling without session timing issues.
|
||||
*/
|
||||
class SubdomainResolver
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
// Skip for API and asset requests
|
||||
if (!$this->shouldProcess($request)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
// Parse domain information using EarlyDomainParser
|
||||
$host = $request->getHost();
|
||||
$domainInfo = EarlyDomainParser::parseDomain($host);
|
||||
|
||||
// Route to appropriate handler based on domain type
|
||||
return match ($domainInfo['type']) {
|
||||
'user-shop' => $this->handleUserShop($request, $next, $domainInfo),
|
||||
'main-shop' => $this->handleMainShop($request, $next, $domainInfo),
|
||||
'main' => $this->handleMainCare($request, $next, $domainInfo),
|
||||
'crm' => $this->handleCrm($request, $next, $domainInfo),
|
||||
'portal' => $this->handlePortal($request, $next, $domainInfo),
|
||||
'checkout' => $this->handleCheckout($request, $next, $domainInfo),
|
||||
default => $this->handleUnknownDomain($request, $domainInfo),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle user shop subdomain (e.g., user.mivita.care)
|
||||
*/
|
||||
private function handleUserShop($request, Closure $next, array $domainInfo)
|
||||
{
|
||||
$subdomain = $domainInfo['subdomain'];
|
||||
$userShop = UserShop::where('slug', $subdomain)->first();
|
||||
|
||||
// Remove subdomain parameter from route
|
||||
if ($request->route('subdomain')) {
|
||||
$request->route()->forgetParameter('subdomain');
|
||||
}
|
||||
|
||||
if (!$userShop) {
|
||||
return $this->handleUnknownDomain($request, $domainInfo);
|
||||
}
|
||||
|
||||
// Validate shop status
|
||||
if (!$userShop->active || !$userShop->user || !$userShop->user->isActiveShop()) {
|
||||
abort(503, 'Shop temporarily unavailable');
|
||||
}
|
||||
|
||||
// Configure session domain based on domain config
|
||||
$this->configureSessionDomain($domainInfo['host']);
|
||||
|
||||
// Set up application context
|
||||
$this->setupUserShopContext($userShop, $subdomain, $domainInfo['host']);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle main shop domain (e.g., mivita.shop)
|
||||
*/
|
||||
private function handleMainShop($request, Closure $next, array $domainInfo)
|
||||
{
|
||||
// Load default shop from domain config
|
||||
$defaultShop = $this->getDefaultShopFromConfig('shop');
|
||||
$userShop = UserShop::where('slug', $defaultShop)->first();
|
||||
|
||||
// Configure session domain based on domain config
|
||||
$this->configureSessionDomain($domainInfo['host']);
|
||||
|
||||
if ($userShop) {
|
||||
$this->setupUserShopContext($userShop, null, $domainInfo['host']);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle main care domain (e.g., mivita.care)
|
||||
*/
|
||||
private function handleMainCare($request, Closure $next, array $domainInfo)
|
||||
{
|
||||
// Configure session domain based on domain config
|
||||
$this->configureSessionDomain($domainInfo['host']);
|
||||
|
||||
// Clear any existing shop session data
|
||||
Session::forget('user_shop');
|
||||
Session::forget('user_shop_domain');
|
||||
|
||||
// Set app URL
|
||||
Config::set('app.url', EarlyDomainParser::getProtocol() . $domainInfo['host']);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle CRM domain (e.g., my.mivita.care)
|
||||
*/
|
||||
private function handleCrm($request, Closure $next, array $domainInfo)
|
||||
{
|
||||
// Configure session domain for CRM
|
||||
$this->configureSessionDomain($domainInfo['host']);
|
||||
|
||||
// Clear shop data for CRM
|
||||
Session::forget('user_shop');
|
||||
Session::forget('user_shop_domain');
|
||||
|
||||
// Set app URL
|
||||
Config::set('app.url', EarlyDomainParser::getProtocol() . $domainInfo['host']);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Portal domain (e.g., in.mivita.care)
|
||||
*/
|
||||
private function handlePortal($request, Closure $next, array $domainInfo)
|
||||
{
|
||||
// Configure session domain for Portal
|
||||
$this->configureSessionDomain($domainInfo['host']);
|
||||
|
||||
// Clear shop data for Portal
|
||||
Session::forget('user_shop');
|
||||
Session::forget('user_shop_domain');
|
||||
|
||||
// Set app URL
|
||||
Config::set('app.url', EarlyDomainParser::getProtocol() . $domainInfo['host']);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Checkout domain (e.g., checkout.mivita.care)
|
||||
*/
|
||||
private function handleCheckout($request, Closure $next, array $domainInfo)
|
||||
{
|
||||
// Configure session domain for Checkout
|
||||
$this->configureSessionDomain($domainInfo['host']);
|
||||
|
||||
// Keep existing shop session data for checkout
|
||||
// Don't clear user_shop - checkout needs to know which shop
|
||||
|
||||
// Set app URL
|
||||
Config::set('app.url', EarlyDomainParser::getProtocol() . $domainInfo['host']);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle unknown domains
|
||||
*/
|
||||
private function handleUnknownDomain($request, array $domainInfo)
|
||||
{
|
||||
// Redirect to main domain using EarlyDomainParser
|
||||
$mainUrl = EarlyDomainParser::getMainUrl();
|
||||
|
||||
return redirect()->away($mainUrl, 301);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up user shop context in session and config
|
||||
*/
|
||||
private function setupUserShopContext(UserShop $userShop, ?string $subdomain = null, string $host = '')
|
||||
{
|
||||
// Put shop data in session
|
||||
Session::put('user_shop', $userShop);
|
||||
|
||||
// Build shop domain URL using EarlyDomainParser
|
||||
$shopDomain = EarlyDomainParser::getProtocol() . $host;
|
||||
Session::put('user_shop_domain', $shopDomain);
|
||||
|
||||
// Set app URL for URL generation
|
||||
Config::set('app.url', rtrim($shopDomain, '/'));
|
||||
|
||||
// Set post route for compatibility
|
||||
Util::setPostRoute('user/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure session domain based on host
|
||||
*/
|
||||
private function configureSessionDomain(string $host): void
|
||||
{
|
||||
// Extract TLD from host to determine session domain
|
||||
if (str_contains($host, config('app.tld_care'))) {
|
||||
Config::set('session.domain', '.' . config('app.domain') . config('app.tld_care'));
|
||||
} elseif (str_contains($host, config('app.tld_shop'))) {
|
||||
Config::set('session.domain', '.' . config('app.domain') . config('app.tld_shop'));
|
||||
} else {
|
||||
// Fallback to care domain
|
||||
Config::set('session.domain', '.' . config('app.domain') . config('app.tld_care'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default shop slug from domain configuration
|
||||
*/
|
||||
private function getDefaultShopFromConfig(string $domainKey): string
|
||||
{
|
||||
return config("domains.domains.{$domainKey}.default_user_shop", 'aloevera');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if request should be processed by this middleware
|
||||
*/
|
||||
private function shouldProcess($request): bool
|
||||
{
|
||||
// Skip API requests
|
||||
if ($request->is('api/*')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip asset requests
|
||||
if ($request->isMethod('GET') && preg_match('/\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$/i', $request->path())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip Laravel internal requests
|
||||
if ($request->is('_debugbar/*')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Domain\EarlyDomainParser;
|
||||
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;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The path to the "home" route for your application.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const HOME = '/';
|
||||
|
||||
/**
|
||||
* The controller namespace for the application.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $namespace = 'App\\Http\\Controllers';
|
||||
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, etc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
|
||||
// $this->configureRateLimiting();
|
||||
|
||||
$this->routes(function () {
|
||||
// API-Routen werden global geladen
|
||||
Route::domain('api.' . config('app.domain') . config('app.tld_care'))
|
||||
->middleware('api')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/api.php'));
|
||||
|
||||
// Web-Routen werden domain-bewusst geladen
|
||||
Route::middleware('web')
|
||||
->namespace($this->namespace)
|
||||
->group(function () {
|
||||
$this->loadDomainAwareRoutes();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt Routen basierend auf der aktuellen Domain mit EarlyDomainParser.
|
||||
* Funktioniert während der Bootstrap-Phase ohne Middleware-Abhängigkeit.
|
||||
*/
|
||||
protected function loadDomainAwareRoutes(): void
|
||||
{
|
||||
// Lade gemeinsame Routen für alle Domains
|
||||
$this->loadSharedRoutes();
|
||||
//$request = $app->make('request');
|
||||
//$request->getHost())
|
||||
// Analysiere den Host der aktuellen Anfrage
|
||||
|
||||
// Parse aktuelle Domain mit Early Domain Parser
|
||||
$domainType = EarlyDomainParser::getCurrentDomainType();
|
||||
|
||||
// Lade domain-spezifische Routen basierend auf Domain-Typ
|
||||
$routesToLoad = match ($domainType) {
|
||||
'main' => ['main'],
|
||||
'main-shop' => ['shop', 'portal'],
|
||||
'user-shop' => ['user-shop', 'portal'],
|
||||
'crm' => ['crm'],
|
||||
'portal' => ['portal'],
|
||||
'checkout' => ['checkout'],
|
||||
'unknown' => ['main'], // Fallback für unbekannte Domains
|
||||
default => ['main'],
|
||||
};
|
||||
|
||||
// Lade die entsprechenden Route-Dateien
|
||||
foreach ($routesToLoad as $routeType) {
|
||||
$this->loadDomainRoutes($routeType, $routeType . '.php');
|
||||
}
|
||||
|
||||
if (config('app.debug')) {
|
||||
\Log::channel('domain')->info('Domain-aware routes loaded', [
|
||||
'domain_type' => $domainType,
|
||||
'routes_loaded' => $routesToLoad,
|
||||
'host' => EarlyDomainParser::parseDomain()['host'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt eine spezifische Routendatei für eine Domain.
|
||||
*/
|
||||
protected function loadDomainRoutes(string $domainType, string $fileName): void
|
||||
{
|
||||
$routePath = base_path('routes/domains/' . $fileName);
|
||||
|
||||
if (file_exists($routePath)) {
|
||||
Route::group([], $routePath);
|
||||
} else {
|
||||
if (config('app.debug')) {
|
||||
\Log::channel('domain')->warning('Domain route file not found', [
|
||||
'domain_type' => $domainType,
|
||||
'file_path' => $routePath,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt Routen, die auf allen Domains verfügbar sein sollen.
|
||||
*/
|
||||
protected function loadSharedRoutes(): void
|
||||
{
|
||||
$sharedRoutePath = base_path('routes/shared/common.php');
|
||||
|
||||
if (file_exists($sharedRoutePath)) {
|
||||
Route::group([], $sharedRoutePath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt alle Domain-Routen für Caching-Zwecke.
|
||||
* Wird verwendet wenn Route-Caching aktiv ist.
|
||||
*/
|
||||
protected function loadAllDomainRoutesForCaching(): void
|
||||
{
|
||||
$domainRoutes = ['main', 'shop', 'user-shop', 'crm', 'portal', 'checkout'];
|
||||
|
||||
foreach ($domainRoutes as $routeType) {
|
||||
$this->loadDomainRoutes($routeType, $routeType . '.php');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the rate limiters for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configureRateLimiting()
|
||||
{
|
||||
RateLimiter::for('api', function (Request $request) {
|
||||
return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
<?php
|
||||
|
||||
namespace App\Domain;
|
||||
|
||||
/**
|
||||
* Early Domain Parser Service
|
||||
*
|
||||
* Provides domain parsing functionality that can be used during
|
||||
* bootstrap phase (RouteServiceProvider) and runtime (Middleware).
|
||||
*
|
||||
* This service caches parsing results per request to avoid duplicate work.
|
||||
*/
|
||||
class EarlyDomainParser
|
||||
{
|
||||
/**
|
||||
* Cache for parsed domain information per request
|
||||
* @var array|null
|
||||
*/
|
||||
private static ?array $cachedDomainInfo = null;
|
||||
|
||||
/**
|
||||
* Cache key (host) for cache invalidation
|
||||
* @var string|null
|
||||
*/
|
||||
private static ?string $cachedHost = null;
|
||||
|
||||
/**
|
||||
* Parse domain information from config/domains.php
|
||||
*
|
||||
* Results are cached per request to avoid duplicate parsing.
|
||||
*
|
||||
* @param string|null $host If null, uses HTTP_HOST or SERVER_NAME
|
||||
* @return array Domain information array
|
||||
*/
|
||||
public static function parseDomain(?string $host = null): array
|
||||
{
|
||||
// Get host from request if not provided
|
||||
if ($host === null) {
|
||||
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';
|
||||
}
|
||||
|
||||
// Remove protocol if present
|
||||
$host = preg_replace('/^https?:\/\//', '', $host);
|
||||
|
||||
// Return cached result if available for same host
|
||||
if (self::$cachedHost === $host && self::$cachedDomainInfo !== null) {
|
||||
return self::$cachedDomainInfo;
|
||||
}
|
||||
|
||||
// Load domains configuration
|
||||
$domains = self::getDomainsConfig();
|
||||
$reservedSubdomains = self::getReservedSubdomains();
|
||||
|
||||
// Check exact matches first (main, shop, crm, portal, checkout)
|
||||
foreach ($domains as $key => $domainConfig) {
|
||||
if ($key === 'user-shop') {
|
||||
continue; // Handle user-shop separately
|
||||
}
|
||||
|
||||
if ($host === $domainConfig['host']) {
|
||||
$domainInfo = [
|
||||
'type' => $domainConfig['type'],
|
||||
'host' => $host,
|
||||
'subdomain' => null,
|
||||
'config_key' => $key,
|
||||
];
|
||||
|
||||
// Cache the result for this request
|
||||
self::$cachedHost = $host;
|
||||
self::$cachedDomainInfo = $domainInfo;
|
||||
|
||||
return $domainInfo;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for user-shop pattern (dynamic subdomains)
|
||||
if (isset($domains['user-shop'])) {
|
||||
$userShopPattern = $domains['user-shop']['host'];
|
||||
$baseDomain = str_replace('{subdomain}.', '', $userShopPattern);
|
||||
|
||||
if (str_ends_with($host, '.' . $baseDomain)) {
|
||||
$subdomain = str_replace('.' . $baseDomain, '', $host);
|
||||
|
||||
// Check if subdomain is not reserved
|
||||
if (!empty($subdomain) && !in_array($subdomain, $reservedSubdomains)) {
|
||||
$domainInfo = [
|
||||
'type' => 'user-shop',
|
||||
'host' => $host,
|
||||
'subdomain' => $subdomain,
|
||||
'config_key' => 'user-shop',
|
||||
];
|
||||
|
||||
// Cache the result for this request
|
||||
self::$cachedHost = $host;
|
||||
self::$cachedDomainInfo = $domainInfo;
|
||||
|
||||
return $domainInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown domain
|
||||
$domainInfo = [
|
||||
'type' => 'unknown',
|
||||
'host' => $host,
|
||||
'subdomain' => null,
|
||||
'config_key' => null,
|
||||
];
|
||||
|
||||
// Cache the result for this request
|
||||
self::$cachedHost = $host;
|
||||
self::$cachedDomainInfo = $domainInfo;
|
||||
|
||||
return $domainInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current domain type quickly (for RouteServiceProvider)
|
||||
*/
|
||||
public static function getCurrentDomainType(): string
|
||||
{
|
||||
$domainInfo = self::parseDomain();
|
||||
return $domainInfo['type'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current domain is a user shop
|
||||
*/
|
||||
public static function isUserShop(): bool
|
||||
{
|
||||
return self::getCurrentDomainType() === 'user-shop';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subdomain for user shops
|
||||
*/
|
||||
public static function getSubdomain(): ?string
|
||||
{
|
||||
$domainInfo = self::parseDomain();
|
||||
return $domainInfo['subdomain'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get domains configuration (early bootstrap safe)
|
||||
*/
|
||||
private static function getDomainsConfig(): array
|
||||
{
|
||||
// Try Laravel config first (if available)
|
||||
if (function_exists('config')) {
|
||||
$config = config('domains.domains');
|
||||
if ($config) {
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Read config file directly
|
||||
$configPath = __DIR__ . '/../../../../config/domains.php';
|
||||
if (file_exists($configPath)) {
|
||||
$config = include $configPath;
|
||||
return $config['domains'] ?? [];
|
||||
}
|
||||
|
||||
// Last resort: Build from environment variables
|
||||
return self::buildConfigFromEnv();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reserved subdomains configuration
|
||||
*/
|
||||
private static function getReservedSubdomains(): array
|
||||
{
|
||||
// Try Laravel config first (if available)
|
||||
if (function_exists('config')) {
|
||||
$reserved = config('domains.reserved_subdomains');
|
||||
if ($reserved) {
|
||||
return $reserved;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Read config file directly
|
||||
$configPath = __DIR__ . '/../../../../config/domains.php';
|
||||
if (file_exists($configPath)) {
|
||||
$config = include $configPath;
|
||||
return $config['reserved_subdomains'] ?? [];
|
||||
}
|
||||
|
||||
// Default reserved subdomains
|
||||
return ['my', 'in', 'checkout', 'www', 'api', 'mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build basic domain configuration from environment variables
|
||||
* Used as fallback when config file is not available
|
||||
*/
|
||||
private static function buildConfigFromEnv(): array
|
||||
{
|
||||
$domain = $_ENV['APP_DOMAIN'] ?? 'mivita';
|
||||
$tldCare = $_ENV['APP_TLD_CARE'] ?? '.care';
|
||||
$tldShop = $_ENV['APP_TLD_SHOP'] ?? '.shop';
|
||||
$crmPrefix = $_ENV['APP_PRE_URL_CRM'] ?? 'my.';
|
||||
$portalPrefix = $_ENV['APP_PRE_URL_PORTAL'] ?? 'in.';
|
||||
$checkoutPrefix = $_ENV['APP_URL_CHECKOUT'] ?? 'checkout.';
|
||||
|
||||
return [
|
||||
'main' => [
|
||||
'host' => $domain . $tldCare,
|
||||
'type' => 'main',
|
||||
],
|
||||
'shop' => [
|
||||
'host' => $domain . $tldShop,
|
||||
'type' => 'main-shop',
|
||||
'default_user_shop' => 'aloevera',
|
||||
],
|
||||
'crm' => [
|
||||
'host' => $crmPrefix . $domain . $tldCare,
|
||||
'type' => 'crm',
|
||||
],
|
||||
'portal' => [
|
||||
'host' => $portalPrefix . $domain . $tldCare,
|
||||
'type' => 'portal',
|
||||
],
|
||||
'checkout' => [
|
||||
'host' => $checkoutPrefix . $domain . $tldCare,
|
||||
'type' => 'checkout',
|
||||
],
|
||||
'user-shop' => [
|
||||
'host' => '{subdomain}.' . $domain . $tldCare,
|
||||
'type' => 'user-shop',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get protocol from configuration
|
||||
*/
|
||||
public static function getProtocol(): string
|
||||
{
|
||||
if (function_exists('config')) {
|
||||
return config('domains.protocol', 'https://');
|
||||
}
|
||||
|
||||
return $_ENV['APP_PROTOCOL'] ?? 'https://';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get main domain URL for redirects
|
||||
*/
|
||||
public static function getMainUrl(): string
|
||||
{
|
||||
$domains = self::getDomainsConfig();
|
||||
$mainHost = $domains['main']['host'] ?? 'localhost';
|
||||
$protocol = self::getProtocol();
|
||||
|
||||
return $protocol . $mainHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the internal cache (useful for testing or special cases)
|
||||
*/
|
||||
public static function clearCache(): void
|
||||
{
|
||||
self::$cachedDomainInfo = null;
|
||||
self::$cachedHost = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if result is cached for current/given host
|
||||
*/
|
||||
public static function isCached(?string $host = null): bool
|
||||
{
|
||||
if ($host === null) {
|
||||
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';
|
||||
$host = preg_replace('/^https?:\/\//', '', $host);
|
||||
}
|
||||
|
||||
return self::$cachedHost === $host && self::$cachedDomainInfo !== null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Geteilte Routen (Shared Routes) - Fixed Version
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Diese Routen sind auf allen Domains verfügbar, die die 'web' Middleware-Gruppe
|
||||
| verwenden. Sie werden vom RouteServiceProvider geladen.
|
||||
|
|
||||
| FIXED: Alle DomainService-Referenzen durch EarlyDomainParser/Config ersetzt
|
||||
|
|
||||
*/
|
||||
|
||||
use App\Domain\EarlyDomainParser;
|
||||
|
||||
// Rechtliche und Kontakt-Routen - Umleitung zur Shop-Domain für checkout.*
|
||||
// Verwendet EarlyDomainParser für domain-awareness
|
||||
$currentDomainType = EarlyDomainParser::getCurrentDomainType();
|
||||
|
||||
if ($currentDomainType === 'checkout') {
|
||||
// Für Checkout-Domain: Umleitung zur Shop-Domain
|
||||
$shopHost = config('domains.domains.shop.host');
|
||||
$protocol = config('domains.protocol', 'https://');
|
||||
$shopBaseUrl = $protocol . $shopHost;
|
||||
|
||||
Route::get('/datenschutz', function () use ($shopBaseUrl) {
|
||||
return redirect()->away($shopBaseUrl . '/datenschutz');
|
||||
})->name('legal.data-protected');
|
||||
|
||||
Route::get('/impressum', function () use ($shopBaseUrl) {
|
||||
return redirect()->away($shopBaseUrl . '/impressum');
|
||||
})->name('legal.imprint');
|
||||
|
||||
Route::get('/agb', function () use ($shopBaseUrl) {
|
||||
return redirect()->away($shopBaseUrl . '/agb');
|
||||
})->name('legal.agb');
|
||||
|
||||
Route::get('/kontakt', function () use ($shopBaseUrl) {
|
||||
return redirect()->away($shopBaseUrl . '/kontakt');
|
||||
})->name('contact.create');
|
||||
|
||||
Route::get('/zahlungsarten', function () use ($shopBaseUrl) {
|
||||
return redirect()->away($shopBaseUrl . '/zahlungsarten');
|
||||
})->name('zahlungsarten');
|
||||
|
||||
Route::get('/versandkosten', function () use ($shopBaseUrl) {
|
||||
return redirect()->away($shopBaseUrl . '/versandkosten');
|
||||
})->name('versandkosten');
|
||||
|
||||
Route::get('/widerrufsrecht', function () use ($shopBaseUrl) {
|
||||
return redirect()->away($shopBaseUrl . '/widerrufsrecht');
|
||||
})->name('widerrufsrecht');
|
||||
}
|
||||
|
||||
// Routes die auf allen Domains verfügbar sind
|
||||
Route::get('register', function () {
|
||||
return 'Register page'; // Placeholder
|
||||
})->name('register');
|
||||
|
||||
Route::get('login', function () {
|
||||
return 'Login page'; // Placeholder
|
||||
})->name('login');
|
||||
|
||||
// Produkt-Bild Route (global verfügbar)
|
||||
Route::get('shop_product_image/{id}/{name?}', function ($id, $name = null) {
|
||||
// Produktbild-Logik
|
||||
return response()->file(storage_path("app/public/products/{$id}.jpg"));
|
||||
})->name('shop_product_image');
|
||||
|
||||
// Checkout-Weiterleitung nur für Domains, die NICHT checkout.* sind
|
||||
if ($currentDomainType !== 'checkout') {
|
||||
|
||||
// Checkout-Domain aus Config laden
|
||||
$checkoutHost = config('domains.domains.checkout.host');
|
||||
$protocol = config('domains.protocol', 'https://');
|
||||
$checkoutBaseUrl = $protocol . $checkoutHost;
|
||||
|
||||
Route::get('/checkout/card/{identifier?}', function ($identifier = null) use ($checkoutBaseUrl) {
|
||||
$path = '/checkout/card/' . ($identifier ? $identifier : '');
|
||||
return redirect()->away($checkoutBaseUrl . $path);
|
||||
})->name('checkout.checkout_card');
|
||||
|
||||
Route::post('/checkout/card/final', function () use ($checkoutBaseUrl) {
|
||||
return redirect()->away($checkoutBaseUrl . '/checkout/card/final');
|
||||
})->name('checkout.checkout_card_final');
|
||||
|
||||
// Weiterleitung für Transaktionsstatus
|
||||
Route::get('/transaction/status/{status?}/{reference?}', function ($status = null, $reference = null) use ($checkoutBaseUrl) {
|
||||
$path = "/transaction/status/" . ($status ?: '') . "/" . ($reference ?: '');
|
||||
return redirect()->away($checkoutBaseUrl . $path);
|
||||
})->name('checkout.transaction_status');
|
||||
|
||||
Route::post('/transaction/status/{status?}/{reference?}', function ($status = null, $reference = null) use ($checkoutBaseUrl) {
|
||||
$path = "/transaction/status/" . ($status ?: '') . "/" . ($reference ?: '');
|
||||
return redirect()->away($checkoutBaseUrl . $path);
|
||||
})->name('checkout.transaction_status_post');
|
||||
|
||||
Route::get('/transaction/approved/{transactionId}/{reference}', function ($transactionId, $reference) use ($checkoutBaseUrl) {
|
||||
return redirect()->away($checkoutBaseUrl . "/transaction/approved/{$transactionId}/{$reference}");
|
||||
})->name('checkout.transaction_approved');
|
||||
}
|
||||
|
||||
// Weitere gemeinsame Routes
|
||||
Route::get('/register/verify/{confirmationCode}', 'HomeController@verify')->name('register_verify');
|
||||
Loading…
Add table
Add a link
Reference in a new issue