mivita/dev/app-bak/Services/DomainService.php
2025-10-20 17:42:08 +02:00

296 lines
8.8 KiB
PHP

<?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 CACHE_TAG_USER_SHOPS = 'user_shops';
private const CACHE_TAG_DOMAIN_PARSING = 'domain_parsing';
private const FIXED_SUBDOMAINS = ['my', 'in', 'checkout'];
private array $domainConfig;
public function __construct(?array $domainConfig = null)
{
$this->domainConfig = $domainConfig ?? config('domains');
}
/**
* Determine the type of subdomain
*/
public function getSubdomainType(string $subdomain): string
{
// Frühe Validierung: Prüfe reservierte Subdomains aus Konfiguration
$reservedSubdomains = $this->domainConfig['reserved_subdomains'] ?? self::FIXED_SUBDOMAINS;
if (in_array($subdomain, $reservedSubdomains)) {
return match ($subdomain) {
'my' => 'crm',
'in' => 'portal',
'checkout' => 'checkout',
default => 'unknown' // Andere reservierte Subdomains sind ungültig
};
}
// Frühe Validierung: Prüfe auf ungültige Zeichen für UserShop-Slugs
if (!preg_match('/^[a-z0-9-]+$/', $subdomain) || strlen($subdomain) < 3) {
return '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) {
// Optimierte Query mit allen Validierungen in einem DB-Call
$userShop = UserShop::where('slug', $slug)
->where('active', true)
->whereHas('user', function ($query) {
$query->whereNotNull('payment_shop')
->where('payment_shop', '>', now());
})
->exists();
return $userShop;
});
}
/**
* Get user shop by slug with caching
*/
public function getUserShop(string $slug): ?UserShop
{
$cacheKey = "user_shop_{$slug}";
return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($slug) {
// Optimierte Query mit allen Validierungen in einem DB-Call
return UserShop::where('slug', $slug)
->where('active', true)
->whereHas('user', function ($query) {
$query->whereNotNull('payment_shop')
->where('payment_shop', '>', now());
})
->with('user')
->first();
});
}
/**
* Parse domain from request and determine context
*/
public function parseDomain(string $host): array
{
// Normalisiere den Host (lowercase)
$host = strtolower(trim($host));
$parts = explode('.', $host);
// Handle different TLD scenarios
if (count($parts) < 2) {
\Log::channel('domain')->warning('Invalid host format', ['host' => $host]);
return [
'type' => 'invalid',
'domain' => $host,
'subdomain' => null,
'tld' => null,
'host' => $host
];
}
// Extract TLD and domain
$tld = '.' . end($parts);
$domain = $parts[count($parts) - 2];
// Check for subdomain
$subdomain = null;
if (count($parts) > 2) {
$subdomain = $parts[0];
if (config('app.debug')) {
\Log::channel('domain')->debug('DomainService: Using extracted subdomain', ['subdomain' => $subdomain, 'host' => $host]);
}
}
// Determine domain type based on subdomain and host
$type = $this->determineDomainType($host, $subdomain);
return [
'type' => $type,
'domain' => $domain,
'subdomain' => $subdomain,
'tld' => $tld,
'host' => $host,
'default_user_shop' => $this->domainConfig['domains']['shop']['default_user_shop'] ?? null
];
}
/**
* Determine domain type based on full host and subdomain
*/
private function determineDomainType(string $host, ?string $subdomain): string
{
// Check against configured domains
foreach ($this->domainConfig['domains'] as $type => $config) {
if (isset($config['host'])) {
// Handle wildcard user-shop pattern
if ($type === 'user-shop') {
$pattern = str_replace('{subdomain}', '([a-z0-9-]+)', $config['host']);
if (preg_match("/^{$pattern}$/", $host)) {
return 'user-shop';
}
} else {
// Exact match for other domains
if ($host === $config['host']) {
return $type;
}
}
}
}
// Additional check for subdomain-based detection
if ($subdomain) {
$subdomainType = $this->getSubdomainType($subdomain);
if ($subdomainType !== 'unknown') {
return $subdomainType;
}
}
return 'unknown';
}
/**
* Build URL for specific domain type
*/
public function buildUrl(string $type, ?string $path = null, ?string $slug = null): string
{
$protocol = $this->domainConfig['protocol'] ?? 'https://';
$domainConfig = $this->domainConfig['domains'][$type] ?? null;
if (!$domainConfig) {
throw new \InvalidArgumentException("Unknown domain type: {$type}");
}
$host = $domainConfig['host'];
// Handle user-shop wildcard
if ($type === 'user-shop') {
if (!$slug) {
throw new \InvalidArgumentException('Slug required for user-shop URLs');
}
$host = str_replace('{subdomain}', $slug, $host);
}
$url = $protocol . $host;
if ($path) {
$url .= '/' . ltrim($path, '/');
}
return $url;
}
/**
* 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}");
}
/**
* Clear all user shop caches
*/
public function clearAllUserShopCaches(): void
{
// In Laravel mit Cache-Tags würde das eleganter funktionieren
// Für jetzt eine einfache Lösung für häufig verwendete Shops
$commonSlugs = ['aloevera']; // Füge häufig verwendete Slugs hinzu
foreach ($commonSlugs as $slug) {
$this->clearUserShopCache($slug);
}
}
/**
* Get default user shop for main domain (fallback)
*/
public function getDefaultUserShop(): ?UserShop
{
$defaultSlug = $this->domainConfig['domains']['shop']['default_user_shop'] ?? 'aloevera';
return $this->getUserShop($defaultSlug);
}
/**
* Validate domain configuration
*/
public function validateConfiguration(): array
{
$errors = [];
// Validate main domains
$requiredDomains = ['main', 'shop', 'crm', 'portal', 'checkout', 'user-shop'];
foreach ($requiredDomains as $domain) {
if (empty($this->domainConfig['domains'][$domain]['host'])) {
$errors[] = "Domain '{$domain}' not configured";
}
}
// Validate protocol
if (empty($this->domainConfig['protocol'])) {
$errors[] = 'Protocol not configured';
}
// Validate reserved subdomains
if (empty($this->domainConfig['reserved_subdomains'])) {
$errors[] = 'Reserved subdomains not configured';
}
// Validate shop default
$defaultShop = $this->domainConfig['domains']['shop']['default_user_shop'] ?? null;
if (!$defaultShop) {
$errors[] = 'Default user shop not configured for shop domain';
}
return $errors;
}
/**
* Check if domain configuration is valid
*/
public function isConfigurationValid(): bool
{
return empty($this->validateConfiguration());
}
}