Konsolidierter Entwicklungsplan aus den zwei Analyse-Dokumenten plus
Umsetzung der Phase-0-Massnahmen (Korrektheit/Security, je mit Tests):
0.1 BasicAuth: timing-sicherer Vergleich (hash_equals, beide Felder ohne
Short-Circuit); Enabled-Check zur Laufzeit via config('auth.basic.*')
statt env() im Bootstrap-Closure (greift jetzt unter config:cache);
Config-Block + .env.example. Fund: auth.basic.* war nie als Config
definiert, Gate war faktisch unkonfigurierbar.
0.2 Legacy-Invoice-"latest"-Abfrage zentralisiert (LegacyInvoice::
scopeLatestFirst + User::latestLegacyInvoice); beide Consumer N+1-frei
umgestellt; unterschiedliches null-Verhalten bewusst erhalten.
0.3 Payment-Option-Eligibility vereinheitlicht (period-aware, strikt):
UserPaymentOption::scopeActiveNow/grandfatheredNow + User::
hasActivePaymentOption; hasActiveBooking/hasUnlimitedPressReleaseQuota
und ApiAccessEligibilityService darauf umgestellt; $hasGrandfathered-
Mislabel behoben.
0.4 Theme->Portal-Vokabular: totes, projektfremdes ThemeMiddleware
geloescht; Mapping in Portal::fromTheme() zentralisiert; fremden
b2in-Default im ThemeServiceProvider auf Portal-Default korrigiert.
0.5 InvoiceNumberGenerator: Erst-Insert race-sicher via insertOrIgnore
(Unique-Index auf circle existiert bereits). CreditWalletService war
bereits abgesichert (Unique-Index + createOrFirst).
Tests: 4 neue Dateien, 5 bestehende auf gueltiges grandfathered_until
angepasst. Volle Suite gruen (793 passed, 1 skipped).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
90 lines
2.9 KiB
PHP
90 lines
2.9 KiB
PHP
<?php
|
|
|
|
namespace App\Providers;
|
|
|
|
use App\Support\DomainAssetContext;
|
|
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
|
|
{
|
|
$this->mergeConfigFrom(
|
|
base_path('config/domains.php'),
|
|
'domains'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Bootstrap services.
|
|
*/
|
|
public function boot(): void
|
|
{
|
|
$host = Request::getHost();
|
|
$themeOverride = Request::get('theme');
|
|
|
|
// Fallback für unbekannte Hosts: Portal-/Admin-Theme dieses Projekts.
|
|
$defaults = [
|
|
'name' => config('app.name'),
|
|
'theme' => 'portal',
|
|
'view_prefix' => 'portal',
|
|
'assets_dir' => 'build/portal',
|
|
'url' => config('app.url'),
|
|
'domain_name' => config('app.domain_name'),
|
|
];
|
|
|
|
$domainConfig = DomainAssetContext::resolve(
|
|
$host,
|
|
$defaults,
|
|
config('domains.domains', []),
|
|
is_string($themeOverride) ? $themeOverride : null,
|
|
);
|
|
|
|
$staticAssetOrigin = DomainAssetContext::staticAssetOrigin($domainConfig);
|
|
$viteDevServerUrl = DomainAssetContext::viteDevServerUrl($domainConfig);
|
|
|
|
config([
|
|
'app.theme' => $domainConfig['theme'],
|
|
'app.view_prefix' => $domainConfig['view_prefix'],
|
|
'app.domain_name' => $domainConfig['domain_name'],
|
|
'app.url' => $domainConfig['url'],
|
|
'app.asset_url' => $staticAssetOrigin,
|
|
]);
|
|
|
|
URL::forceRootUrl($domainConfig['url']);
|
|
URL::forceScheme(parse_url($domainConfig['url'], PHP_URL_SCHEME) ?: 'https');
|
|
|
|
/** @var UrlGenerator $urlGenerator */
|
|
$urlGenerator = app('url');
|
|
$urlGenerator->useAssetOrigin($staticAssetOrigin);
|
|
|
|
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', $staticAssetOrigin);
|
|
View::share('viteDevServerUrl', $viteDevServerUrl);
|
|
|
|
if (! app()->runningInConsole()) {
|
|
if (isset($domainConfig['assets_dir'])) {
|
|
DomainAssetContext::configureVite($domainConfig);
|
|
}
|
|
|
|
if (app()->environment('local')) {
|
|
config(['app.vite_dev_server_url' => $viteDevServerUrl]);
|
|
} else {
|
|
Vite::useScriptTagAttributes(['crossorigin' => false]);
|
|
Vite::useStyleTagAttributes(['crossorigin' => false]);
|
|
}
|
|
}
|
|
}
|
|
}
|