Umsetzung von AP0-AP6 aus dev/2026-08-28/entwicklungsplan.md sowie der vorab gefundenen Befunde T1 und B1-B13. Befunde - CmsContentSeeder überschrieb bei jedem Lauf alle redaktionellen Inhalte; jetzt Redaktionsschutz plus gezielter Relaunch-Seeder (B1) - Magazin-CMS war nicht ans Frontend angebunden, Artikel jetzt über Slugs mit 301 von den alten numerischen URLs (B2) - Vier veröffentlichte Display-Playlists zeigten auf b2in.test und wären live nicht ladbar gewesen (B13) - Info-Tablet speicherte geleerte Öffnungszeiten nicht (T1) - /service und /portfolio lieferten seit Februar 500 (B12) Website - Startseite neu strukturiert und umgetextet, eigene Seite /marken mit Detailseite je Kernmarke, /netzwerk und Altrouten leiten dorthin - Galerie je Marke, getrennt nach realisierten Projekten und Visualisierungen; ohne gesetzte Kategorie gilt ein Bild als Entwurf - Seite /referenzen, gespeist aus den Markengalerien - Showroom-Display im 9:16-Rahmen über eine schreibgeschützte Embed-Route ohne Preview-Token - Terminanfrage-Widget auf allen Seiten, Markenauswahl aus dem CMS, WhatsApp per Klick-zum-Chat-Link mit vorbereitetem Text - FAQ, Über-uns, Kontakt und Magazin inhaltlich auf Interior umgestellt Sonstiges - Vorschau-Instanzen liefern noindex und verlinken sich bei unbekanntem Host auf sich selbst statt auf eine fremde Domain - Seeder-Zugangsdaten aus dem Code in die Konfiguration (B8) - Ablauf für die Vorschau: dev/2026-08-28/preview-deployment.md 833 Tests grün (3084 Assertions), Pint sauber. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
123 lines
5 KiB
PHP
123 lines
5 KiB
PHP
<?php
|
||
|
||
namespace App\Providers;
|
||
|
||
use Illuminate\Routing\UrlGenerator;
|
||
use Illuminate\Support\Facades\Request;
|
||
use Illuminate\Support\Facades\URL;
|
||
use Illuminate\Support\Facades\View;
|
||
use Illuminate\Support\Facades\Vite;
|
||
use Illuminate\Support\ServiceProvider;
|
||
|
||
class ThemeServiceProvider extends ServiceProvider
|
||
{
|
||
/**
|
||
* Register services.
|
||
*/
|
||
public function register(): void
|
||
{
|
||
// Registriere die domains.php Konfigurationsdatei
|
||
$this->mergeConfigFrom(
|
||
base_path('config/domains.php'),
|
||
'domains'
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Bootstrap services.
|
||
*/
|
||
public function boot(): void
|
||
{
|
||
$host = Request::getHost(); // is domain_name
|
||
$themeOverride = Request::get('theme'); // Allow theme override via URL parameter
|
||
|
||
// Standard-Werte für Domain, die nicht in der Konfiguration sind
|
||
$domainConfig = [
|
||
'name' => config('app.name'),
|
||
'theme' => 'b2in',
|
||
'view_prefix' => 'b2in',
|
||
'assets_dir' => 'build/b2in',
|
||
'url' => config('app.url'),
|
||
'domain_name' => config('app.domain_name'),
|
||
];
|
||
|
||
// Lade die Domain-Konfiguration
|
||
$confiDomains = config('domains.domains');
|
||
|
||
// Suche nach der aktuellen Domain in der Konfiguration
|
||
foreach ($confiDomains as $name => $config) {
|
||
if (is_array($config) && isset($config['domain_name']) && $config['domain_name'] === $host) {
|
||
$domainConfig = array_merge($domainConfig, $config);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Allow theme override via URL parameter (for testing)
|
||
if ($themeOverride && isset($confiDomains[$themeOverride])) {
|
||
$domainConfig = array_merge($domainConfig, $confiDomains[$themeOverride]);
|
||
}
|
||
|
||
// Unbekannter Host (Vorschau-, Staging- oder IP-Aufruf): Bis hierher stünde in `url` noch
|
||
// APP_URL bzw. der Vorgabewert aus config/domains.php – also eine *fremde* Domain. Alle
|
||
// Assets und Links würden dorthin zeigen, obwohl die Instanz unter einem anderen Namen
|
||
// läuft. Außerhalb von `production` gilt deshalb der tatsächliche Request-Origin.
|
||
//
|
||
// In `production` bleibt es bewusst bei der konfigurierten URL: dort darf ein gefälschter
|
||
// Host-Header keine Links auf eine fremde Domain erzeugen.
|
||
if ($domainConfig['domain_name'] !== $host && config('app.env') !== 'production') {
|
||
$domainConfig['domain_name'] = $host;
|
||
$domainConfig['url'] = Request::getSchemeAndHttpHost();
|
||
}
|
||
|
||
// Dynamische ASSET_URL basierend auf der aktuellen Domain setzen
|
||
// Verhindert CORS-Probleme, da Assets immer von derselben Domain geladen werden
|
||
$assetUrl = $domainConfig['url'];
|
||
|
||
// Grundlegende Konfiguration im Anwendungskontext verfügbar machen
|
||
config([
|
||
'app.theme' => $domainConfig['theme'],
|
||
'app.view_prefix' => $domainConfig['view_prefix'],
|
||
'app.domain_name' => $domainConfig['domain_name'],
|
||
'app.url' => $domainConfig['url'],
|
||
'app.asset_url' => $assetUrl, // Dynamische Asset-URL für die aktuelle Domain
|
||
]);
|
||
|
||
// URL-Generator für die aktuelle Domain konfigurieren
|
||
// Dies ist wichtig, damit asset() und url() die richtige Domain verwenden
|
||
URL::forceRootUrl($domainConfig['url']);
|
||
URL::forceScheme(parse_url($domainConfig['url'], PHP_URL_SCHEME) ?: 'https');
|
||
|
||
// WICHTIG: Asset-Root direkt im UrlGenerator setzen
|
||
// Der asset() Helper verwendet einen separaten Asset-Root
|
||
/** @var UrlGenerator $urlGenerator */
|
||
$urlGenerator = app('url');
|
||
$urlGenerator->useAssetOrigin($assetUrl);
|
||
|
||
// Spezifischere Daten für die Views verfügbar machen
|
||
View::share('theme', $domainConfig['theme']);
|
||
View::share('viewPrefix', $domainConfig['view_prefix']);
|
||
View::share('domainName', $domainConfig['domain_name']);
|
||
View::share('domainConfig', $domainConfig);
|
||
View::share('domainUrl', $domainConfig['url']);
|
||
View::share('assetUrl', $assetUrl);
|
||
|
||
// Vite-Assets-Konfiguration für die aktuelle Domain
|
||
if (! app()->runningInConsole()) {
|
||
if (isset($domainConfig['assets_dir'])) {
|
||
Vite::useBuildDirectory($domainConfig['assets_dir']);
|
||
}
|
||
|
||
if (app()->environment('local')) {
|
||
// Entwicklung: Vite Dev Server mit HMR
|
||
$viteDevServerUrl = env('VITE_DEV_SERVER_URL', 'https://assets.b2in.test');
|
||
Vite::useHotFile(public_path('hot'));
|
||
config(['app.vite_dev_server_url' => $viteDevServerUrl]);
|
||
View::share('viteDevServerUrl', $viteDevServerUrl);
|
||
} else {
|
||
// Produktion: Assets von der aktuellen Domain laden (kein CORS nötig)
|
||
Vite::useScriptTagAttributes(['crossorigin' => false]);
|
||
Vite::useStyleTagAttributes(['crossorigin' => false]);
|
||
}
|
||
}
|
||
}
|
||
}
|