presseportale/app/Support/DomainAssetContext.php
Kevin Adametz acf67211bf web: www-Hosts lösen aufs Apex-Domain-Theme auf statt aufs Portal-Fallback
Die Domain-Auflösung matchte den Host exakt gegen domain_name —
www.presseecho.de fiel dadurch aufs Portal-Default-Theme zurück, dessen
theme-portal.css im Web-Build nicht existiert. Alle Route::view-Seiten
ohne Controller (Impressum, Datenschutz, AGB, FAQ, …) antworteten auf
www-Hosts mit 500 (ViteException, Dauereintrag im Live-Log); die
Controller-Seiten traf es nicht, weil WebPortalContext den Host tolerant
matcht. resolve() normalisiert Hosts jetzt (lowercase, www. entfernt) auf
beiden Seiten des Vergleichs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 15:13:33 +00:00

194 lines
5.8 KiB
PHP

<?php
namespace App\Support;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Vite;
class DomainAssetContext
{
/**
* Wendet die Domain-/Theme-Konfiguration einer öffentlichen Web-Ausgabe an
* (config + View::share + Vite) und liefert die aufgelöste Domain-Config.
* Single Source of Truth für die zuvor je Route-Closure wiederholte Logik.
*
* @return array<string, mixed>
*/
public static function applyWebDomainConfig(string $domainKey): array
{
/** @var array<string, mixed> $domainConfig */
$domainConfig = config("domains.domains.{$domainKey}", []);
config([
'app.theme' => $domainConfig['theme'] ?? $domainKey,
'app.view_prefix' => $domainConfig['view_prefix'] ?? 'web',
'app.domain_name' => $domainConfig['domain_name'] ?? request()->getHost(),
'app.url' => $domainConfig['url'] ?? config('app.url'),
]);
View::share('theme', $domainConfig['theme'] ?? $domainKey);
View::share('viewPrefix', $domainConfig['view_prefix'] ?? 'web');
View::share('domainName', $domainConfig['domain_name'] ?? request()->getHost());
View::share('domainConfig', $domainConfig);
View::share('domainUrl', $domainConfig['url'] ?? config('app.url'));
View::share('assetUrl', self::staticAssetOrigin($domainConfig));
self::configureVite($domainConfig);
return $domainConfig;
}
/**
* @param array<string, mixed> $defaults
* @param array<string, array<string, mixed>> $configuredDomains
* @return array<string, mixed>
*/
public static function resolve(
string $host,
array $defaults,
array $configuredDomains,
?string $themeOverride = null,
?Request $request = null,
): array {
if ($themeOverride !== null && isset($configuredDomains[$themeOverride])) {
return array_merge($defaults, $configuredDomains[$themeOverride]);
}
// www-Varianten zählen als ihre Apex-Domain: Ohne Normalisierung
// fiele z. B. www.presseecho.de auf das Portal-Default-Theme zurück,
// dessen theme-portal.css im Web-Build nicht existiert → 500
// (ViteException) auf allen Route::view-Seiten (Impressum, AGB, …).
$host = self::normalizedHost($host);
$portalHost = self::normalizedHost((string) config('domains.domain_portal'));
if ($host === $portalHost) {
$request ??= request();
if (self::isBackendRequest($request)) {
return array_merge($defaults, $configuredDomains['portal'] ?? $defaults);
}
return array_merge($defaults, $configuredDomains['pressekonto'] ?? $defaults);
}
foreach ($configuredDomains as $config) {
if (! is_array($config)) {
continue;
}
$domainName = $config['domain_name'] ?? null;
if (is_string($domainName) && self::normalizedHost($domainName) === $host) {
return array_merge($defaults, $config);
}
}
return $defaults;
}
/**
* Kleingeschriebener Host ohne führendes "www.".
*/
private static function normalizedHost(string $host): string
{
$host = strtolower(trim($host));
return str_starts_with($host, 'www.') ? substr($host, 4) : $host;
}
public static function isBackendRequest(Request $request): bool
{
if ($request->routeIs(
'dashboard',
'login',
'register',
'password.*',
'verification.*',
'two-factor.*',
'magic-links.*',
'me.*',
'settings.*',
)) {
return true;
}
$backendPrefixes = [
'admin',
'customer',
'dashboard',
'login',
'register',
'forgot-password',
'reset-password',
'magic-login',
'user',
'settings',
'flux',
'livewire',
];
$path = trim($request->path(), '/');
foreach ($backendPrefixes as $prefix) {
if ($path === $prefix || str_starts_with($path, "{$prefix}/")) {
return true;
}
}
return false;
}
public static function usesWebAssets(string $assetsDir): bool
{
return $assetsDir === 'build/web';
}
public static function hotFilePath(string $assetsDir): string
{
return self::usesWebAssets($assetsDir)
? public_path('hot-web')
: public_path('hot-portal');
}
/**
* @param array<string, mixed> $domainConfig
*/
public static function configureVite(array $domainConfig): void
{
$assetsDir = (string) ($domainConfig['assets_dir'] ?? 'build/portal');
Vite::useBuildDirectory($assetsDir);
Vite::useHotFile(self::hotFilePath($assetsDir));
}
/**
* @param array<string, mixed> $domainConfig
*/
public static function staticAssetOrigin(array $domainConfig): string
{
return (string) ($domainConfig['url'] ?? config('app.url'));
}
/**
* @param array<string, mixed> $domainConfig
*/
public static function viteDevServerUrl(array $domainConfig): string
{
if (isset($domainConfig['asset_url'])) {
return (string) $domainConfig['asset_url'];
}
$assetsDir = (string) ($domainConfig['assets_dir'] ?? 'build/portal');
return self::usesWebAssets($assetsDir)
? 'https://assets.pressekonto.test'
: 'https://assets.pressekonto.test';
}
public static function isViteDevServerRunning(string $assetsDir): bool
{
return is_file(self::hotFilePath($assetsDir));
}
}