Multi-Domain-Asset-Infrastruktur: geteilte Vite-Konfiguration und DomainAssetContext
- vite.shared.js als gemeinsame Quelle fuer Ports, Hot-Files, HMR-Hosts und CORS-Origins der beiden Vite-Builds (Portal/Web) - App\Support\DomainAssetContext kapselt die Vite-Build-Directory- Konfiguration pro Domain (ThemeServiceProvider + Auth-Layout nutzen ihn) - Tailwind-Portal-Content-Globs auf die tatsaechliche View-Struktur gezogen - Dev-Beispiel-Routen + Tests (DomainAssetContextTest, DevExampleRoutesTest) - Aufraeumen: versehentliche Leerdatei dev:web entfernt Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
4bb9094207
commit
0efabaf446
15 changed files with 485 additions and 109 deletions
|
|
@ -16,4 +16,23 @@ enum Portal: string
|
|||
self::Both => 'Beide Portale',
|
||||
};
|
||||
}
|
||||
|
||||
public function abbreviation(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Presseecho => 'PE',
|
||||
self::Businessportal24 => 'B24',
|
||||
self::Both => 'PE+B24',
|
||||
};
|
||||
}
|
||||
|
||||
public static function stripTrailingAbbreviation(string $value): string
|
||||
{
|
||||
$abbreviations = implode('|', array_map(
|
||||
fn (self $portal): string => preg_quote($portal->abbreviation(), '/'),
|
||||
self::cases(),
|
||||
));
|
||||
|
||||
return trim((string) preg_replace('/\s*\(('.$abbreviations.')\)\s*$/u', '', $value));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Support\DomainAssetContext;
|
||||
use Illuminate\Routing\UrlGenerator;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
|
|
@ -16,7 +17,6 @@ class ThemeServiceProvider extends ServiceProvider
|
|||
*/
|
||||
public function register(): void
|
||||
{
|
||||
// Registriere die domains.php Konfigurationsdatei
|
||||
$this->mergeConfigFrom(
|
||||
base_path('config/domains.php'),
|
||||
'domains'
|
||||
|
|
@ -28,11 +28,10 @@ class ThemeServiceProvider extends ServiceProvider
|
|||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$host = Request::getHost(); // is domain_name
|
||||
$themeOverride = Request::get('theme'); // Allow theme override via URL parameter
|
||||
$host = Request::getHost();
|
||||
$themeOverride = Request::get('theme');
|
||||
|
||||
// Standard-Werte für Domain, die nicht in der Konfiguration sind
|
||||
$domainConfig = [
|
||||
$defaults = [
|
||||
'name' => config('app.name'),
|
||||
'theme' => 'b2in',
|
||||
'view_prefix' => 'b2in',
|
||||
|
|
@ -41,68 +40,47 @@ class ThemeServiceProvider extends ServiceProvider
|
|||
'domain_name' => config('app.domain_name'),
|
||||
];
|
||||
|
||||
// Lade die Domain-Konfiguration
|
||||
$confiDomains = config('domains.domains');
|
||||
$domainConfig = DomainAssetContext::resolve(
|
||||
$host,
|
||||
$defaults,
|
||||
config('domains.domains', []),
|
||||
is_string($themeOverride) ? $themeOverride : null,
|
||||
);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
$staticAssetOrigin = DomainAssetContext::staticAssetOrigin($domainConfig);
|
||||
$viteDevServerUrl = DomainAssetContext::viteDevServerUrl($domainConfig);
|
||||
|
||||
// Allow theme override via URL parameter (for testing)
|
||||
if ($themeOverride && isset($confiDomains[$themeOverride])) {
|
||||
$domainConfig = array_merge($domainConfig, $confiDomains[$themeOverride]);
|
||||
}
|
||||
|
||||
// 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
|
||||
'app.asset_url' => $staticAssetOrigin,
|
||||
]);
|
||||
|
||||
// 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);
|
||||
$urlGenerator->useAssetOrigin($staticAssetOrigin);
|
||||
|
||||
// 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);
|
||||
View::share('assetUrl', $staticAssetOrigin);
|
||||
View::share('viteDevServerUrl', $viteDevServerUrl);
|
||||
|
||||
// Vite-Assets-Konfiguration für die aktuelle Domain
|
||||
if (! app()->runningInConsole()) {
|
||||
if (isset($domainConfig['assets_dir'])) {
|
||||
Vite::useBuildDirectory($domainConfig['assets_dir']);
|
||||
DomainAssetContext::configureVite($domainConfig);
|
||||
}
|
||||
|
||||
if (app()->environment('local')) {
|
||||
// Entwicklung: Vite Dev Server mit HMR
|
||||
$viteDevServerUrl = env('VITE_DEV_SERVER_URL', 'https://assets.pressekonto.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]);
|
||||
}
|
||||
|
|
|
|||
145
app/Support/DomainAssetContext.php
Normal file
145
app/Support/DomainAssetContext.php
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Vite;
|
||||
|
||||
class DomainAssetContext
|
||||
{
|
||||
/**
|
||||
* @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]);
|
||||
}
|
||||
|
||||
$portalHost = (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;
|
||||
}
|
||||
|
||||
if (($config['domain_name'] ?? null) === $host) {
|
||||
return array_merge($defaults, $config);
|
||||
}
|
||||
}
|
||||
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
public static function isBackendRequest(Request $request): bool
|
||||
{
|
||||
if ($request->routeIs(
|
||||
'dashboard',
|
||||
'login',
|
||||
'register',
|
||||
'password.*',
|
||||
'verification.*',
|
||||
'two-factor.*',
|
||||
'magic-links.*',
|
||||
'me.*',
|
||||
'settings.*',
|
||||
'press-releases.preview',
|
||||
)) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
@ -26,10 +26,10 @@ return [
|
|||
'domain_name' => env('APP_PORTAL_NAME', 'pressekonto.test'),
|
||||
'url' => env('APP_PORTAL_URL', 'https://pressekonto.test'),
|
||||
'asset_url' => env('APP_PORTAL_ASSET_URL', 'https://assets.pressekonto.test'),
|
||||
'theme' => 'main',
|
||||
'theme' => 'portal',
|
||||
'view_prefix' => 'portal',
|
||||
'assets_dir' => 'build/portal',
|
||||
'description' => 'Backend Pressekonto',
|
||||
'description' => 'Backend/Admin auf pressekonto.test',
|
||||
'color_scheme' => [
|
||||
'primary' => env('APP_PORTAL_PRIMARY', '#526266'), //
|
||||
'secondary' => env('APP_PORTAL_SECONDARY', '#82a0a7'), //
|
||||
|
|
|
|||
0
dev:web
0
dev:web
|
|
@ -55,11 +55,11 @@ services:
|
|||
- "traefik.http.routers.businessportal.tls=true"
|
||||
- "traefik.http.routers.businessportal.service=pressekonto-service-prc"
|
||||
|
||||
# Asset Domain für Vite-Server Portal (Port 5177)
|
||||
- "traefik.http.routers.assets-portal.rule=Host(`assets.pressekonto.test`)"
|
||||
- "traefik.http.routers.assets-portal.entrypoints=websecure"
|
||||
- "traefik.http.routers.assets-portal.tls=true"
|
||||
- "traefik.http.routers.assets-portal.service=assets-portal-service-prc"
|
||||
# Asset Domain für Vite-Server Portal/Admin (Port 5177)
|
||||
- "traefik.http.routers.assets-pressekonto.rule=Host(`assets.pressekonto.test`)"
|
||||
- "traefik.http.routers.assets-pressekonto.entrypoints=websecure"
|
||||
- "traefik.http.routers.assets-pressekonto.tls=true"
|
||||
- "traefik.http.routers.assets-pressekonto.service=assets-portal-service-prc"
|
||||
|
||||
# Asset Domain für Vite-Server Presseecho
|
||||
- "traefik.http.routers.assets-presseecho.rule=Host(`assets.presseecho.test`)"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "echo 'Bitte spezifischen Dev-Server starten: npm run dev:portal ODER npm run dev:web ODER npm run dev:all'",
|
||||
"dev": "echo 'Dev-Server: npm run dev:portal (Admin/FluxUI) | npm run dev:web (pressekonto Hub + presseecho + businessportal24) | npm run dev:all (beides)'",
|
||||
"dev:portal": "vite --config vite.portal.config.js",
|
||||
"dev:web": "vite --config vite.web.config.js",
|
||||
"dev:all": "concurrently \"npm run dev:portal\" \"npm run dev:web\" --names \"PORTAL,WEB\" --prefix-colors \"cyan,magenta\"",
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@
|
|||
]);
|
||||
$themeCssPath = \App\Helpers\ThemeHelper::getThemeCssPath();
|
||||
$assetsDir = config('domains.domains.pressekonto.assets_dir', 'build/web');
|
||||
\Illuminate\Support\Facades\Vite::useBuildDirectory($assetsDir);
|
||||
\App\Support\DomainAssetContext::configureVite([
|
||||
'assets_dir' => $assetsDir,
|
||||
]);
|
||||
@endphp
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use App\Http\Controllers\PressReleasePreviewController;
|
|||
use App\Models\Category;
|
||||
use App\Models\Company;
|
||||
use App\Models\PressRelease;
|
||||
use App\Support\DomainAssetContext;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
|
@ -33,7 +34,9 @@ $applyWebDomainConfig = static function (string $domainKey): array {
|
|||
View::share('domainName', $domainConfig['domain_name'] ?? request()->getHost());
|
||||
View::share('domainConfig', $domainConfig);
|
||||
View::share('domainUrl', $domainConfig['url'] ?? config('app.url'));
|
||||
View::share('assetUrl', $domainConfig['url'] ?? config('app.url'));
|
||||
View::share('assetUrl', DomainAssetContext::staticAssetOrigin($domainConfig));
|
||||
|
||||
DomainAssetContext::configureVite($domainConfig);
|
||||
|
||||
return $domainConfig;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,10 +10,18 @@ const defaultTheme = require("tailwindcss/defaultTheme");
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./resources/views/portal/**/*.blade.php",
|
||||
"./resources/views/layouts/portal/**/*.blade.php",
|
||||
"./resources/views/components/**/*.blade.php",
|
||||
"./app/Livewire/Portal/**/*.php",
|
||||
"./resources/views/livewire/admin/**/*.blade.php",
|
||||
"./resources/views/livewire/customer/**/*.blade.php",
|
||||
"./resources/views/livewire/components/**/*.blade.php",
|
||||
"./resources/views/livewire/settings/**/*.blade.php",
|
||||
"./resources/views/livewire/auth/**/*.blade.php",
|
||||
"./resources/views/layouts/**/*.blade.php",
|
||||
"./resources/views/components/layouts/**/*.blade.php",
|
||||
"./resources/views/components/portal/**/*.blade.php",
|
||||
"./resources/views/components/settings/**/*.blade.php",
|
||||
"./resources/views/partials/**/*.blade.php",
|
||||
"./resources/views/admin/**/*.blade.php",
|
||||
"./app/Livewire/**/*.php",
|
||||
"./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php",
|
||||
"./vendor/livewire/flux-pro/stubs/**/*.blade.php",
|
||||
"./vendor/livewire/flux/stubs/**/*.blade.php",
|
||||
|
|
|
|||
84
tests/Feature/DomainAssetContextTest.php
Normal file
84
tests/Feature/DomainAssetContextTest.php
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
use App\Support\DomainAssetContext;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
test('pressekonto homepage resolves to web assets', function () {
|
||||
$defaults = ['theme' => 'fallback', 'assets_dir' => 'build/b2in'];
|
||||
$domains = config('domains.domains');
|
||||
|
||||
$config = DomainAssetContext::resolve(
|
||||
'pressekonto.test',
|
||||
$defaults,
|
||||
$domains,
|
||||
null,
|
||||
Request::create('https://pressekonto.test/', 'GET'),
|
||||
);
|
||||
|
||||
expect($config['theme'])->toBe('pressekonto')
|
||||
->and($config['assets_dir'])->toBe('build/web');
|
||||
});
|
||||
|
||||
test('pressekonto admin routes resolve to portal assets', function () {
|
||||
$defaults = ['theme' => 'fallback', 'assets_dir' => 'build/b2in'];
|
||||
$domains = config('domains.domains');
|
||||
|
||||
$config = DomainAssetContext::resolve(
|
||||
'pressekonto.test',
|
||||
$defaults,
|
||||
$domains,
|
||||
null,
|
||||
Request::create('https://pressekonto.test/admin/users', 'GET'),
|
||||
);
|
||||
|
||||
expect($config['theme'])->toBe('portal')
|
||||
->and($config['assets_dir'])->toBe('build/portal')
|
||||
->and($config['asset_url'])->toBe('https://assets.pressekonto.test');
|
||||
});
|
||||
|
||||
test('presseecho resolves to its own web theme', function () {
|
||||
$defaults = ['theme' => 'fallback', 'assets_dir' => 'build/b2in'];
|
||||
$domains = config('domains.domains');
|
||||
|
||||
$config = DomainAssetContext::resolve(
|
||||
'presseecho.test',
|
||||
$defaults,
|
||||
$domains,
|
||||
);
|
||||
|
||||
expect($config['theme'])->toBe('presseecho')
|
||||
->and($config['assets_dir'])->toBe('build/web');
|
||||
});
|
||||
|
||||
test('hot file path depends on asset bundle', function () {
|
||||
expect(DomainAssetContext::hotFilePath('build/web'))
|
||||
->toEndWith('hot-web')
|
||||
->and(DomainAssetContext::hotFilePath('build/portal'))
|
||||
->toEndWith('hot-portal');
|
||||
});
|
||||
|
||||
test('static asset origin always uses main domain not vite subdomain', function () {
|
||||
expect(DomainAssetContext::staticAssetOrigin([
|
||||
'url' => 'https://pressekonto.test',
|
||||
'asset_url' => 'https://assets.pressekonto.test',
|
||||
]))->toBe('https://pressekonto.test');
|
||||
});
|
||||
|
||||
test('vite dev server url uses asset subdomain from domain config', function () {
|
||||
expect(DomainAssetContext::viteDevServerUrl([
|
||||
'url' => 'https://pressekonto.test',
|
||||
'asset_url' => 'https://assets.pressekonto.test',
|
||||
'assets_dir' => 'build/portal',
|
||||
]))->toBe('https://assets.pressekonto.test');
|
||||
});
|
||||
|
||||
test('backend request detection covers admin and livewire paths', function () {
|
||||
expect(DomainAssetContext::isBackendRequest(Request::create('/admin/users')))
|
||||
->toBeTrue()
|
||||
->and(DomainAssetContext::isBackendRequest(Request::create('/livewire/update', 'POST')))
|
||||
->toBeTrue()
|
||||
->and(DomainAssetContext::isBackendRequest(Request::create('/kategorien')))
|
||||
->toBeFalse()
|
||||
->and(DomainAssetContext::isBackendRequest(Request::create('/')))
|
||||
->toBeFalse();
|
||||
});
|
||||
7
tests/Feature/Web/DevExampleRoutesTest.php
Normal file
7
tests/Feature/Web/DevExampleRoutesTest.php
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
test('example', function () {
|
||||
$response = $this->get('/');
|
||||
|
||||
$response->assertStatus(200);
|
||||
});
|
||||
|
|
@ -1,24 +1,25 @@
|
|||
/**
|
||||
* Vite-Konfiguration für Backend (Portal)
|
||||
* - Domain: pressekonto.test
|
||||
* Vite-Konfiguration für Backend/Admin (FluxUI)
|
||||
* - Domain: pressekonto.test (Admin, Customer, Auth)
|
||||
* - Asset-Subdomain: assets.pressekonto.test
|
||||
* - Port: 5177
|
||||
* - Verwendet FluxUI
|
||||
* - Build-Verzeichnis: public/build/portal
|
||||
* - Build: public/build/portal
|
||||
*
|
||||
* Öffentliche Hub-Landing und Frontends → vite.web.config.js (dev:web)
|
||||
*
|
||||
* Starten mit: npm run dev:portal
|
||||
*/
|
||||
import { defineConfig } from "vite";
|
||||
import laravel from "laravel-vite-plugin";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
|
||||
const httpsConfig =
|
||||
process.env.NODE_ENV === "production"
|
||||
? {
|
||||
// In Produktion: echte Zertifikate verwenden
|
||||
key: process.env.SSL_KEY_PATH,
|
||||
cert: process.env.SSL_CERT_PATH,
|
||||
}
|
||||
: true; // Self-signed für Entwicklung
|
||||
import {
|
||||
PORTAL_HMR_HOST,
|
||||
PORTAL_HOT_FILE,
|
||||
PORTAL_PORT,
|
||||
createWatchIgnored,
|
||||
portalRefreshPaths,
|
||||
portalWatchDirs,
|
||||
} from "./vite.shared.js";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
|
|
@ -28,24 +29,28 @@ export default defineConfig({
|
|||
"resources/js/app.js",
|
||||
"resources/js/portal-form-hooks.js",
|
||||
],
|
||||
refresh: ["resources/views/portal/**/*.blade.php"],
|
||||
refresh: portalRefreshPaths,
|
||||
hotFile: PORTAL_HOT_FILE,
|
||||
}),
|
||||
tailwindcss({
|
||||
config: "./tailwind.portal.config.js",
|
||||
}),
|
||||
],
|
||||
server: {
|
||||
https: false, // Traefik übernimmt SSL, Vite läuft intern auf HTTP
|
||||
https: false,
|
||||
cors: true,
|
||||
host: "0.0.0.0",
|
||||
port: 5174, // oder 5175
|
||||
hmr: {
|
||||
host: "assets.pressekonto.test",
|
||||
protocol: "wss", // Explizit wss für WebSocket Secure
|
||||
// WICHTIG: Die 'port'-Angabe hier entfernen!
|
||||
// Der Browser soll den Standard-Port (443) von Traefik nutzen.
|
||||
port: PORTAL_PORT,
|
||||
strictPort: true,
|
||||
allowedHosts: ["pressekonto.test", PORTAL_HMR_HOST, "localhost", "0.0.0.0"],
|
||||
watch: {
|
||||
ignored: createWatchIgnored(portalWatchDirs),
|
||||
},
|
||||
// Das origin ist nicht mehr notwendig, da der HMR-Port wegfällt.
|
||||
hmr: {
|
||||
host: PORTAL_HMR_HOST,
|
||||
protocol: "wss",
|
||||
},
|
||||
origin: `https://${PORTAL_HMR_HOST}`,
|
||||
},
|
||||
build: {
|
||||
outDir: "public/build/portal",
|
||||
|
|
|
|||
126
vite.shared.js
Normal file
126
vite.shared.js
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const projectRoot = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
export const PORTAL_PORT = 5177;
|
||||
export const WEB_PORT = 5178;
|
||||
|
||||
export const PORTAL_HOT_FILE = "public/hot-portal";
|
||||
export const WEB_HOT_FILE = "public/hot-web";
|
||||
|
||||
export const PORTAL_HMR_HOST = "assets.pressekonto.test";
|
||||
export const WEB_HMR_HOSTS = [
|
||||
"assets.presseecho.test",
|
||||
"assets.businessportal24.test",
|
||||
];
|
||||
|
||||
export const WEB_CORS_ORIGINS = [
|
||||
"https://pressekonto.test",
|
||||
"https://assets.pressekonto.test",
|
||||
"https://presseecho.test",
|
||||
"https://assets.presseecho.test",
|
||||
"https://businessportal24.test",
|
||||
"https://assets.businessportal24.test",
|
||||
];
|
||||
|
||||
export const WEB_ALLOWED_HOSTS = [
|
||||
"pressekonto.test",
|
||||
"assets.pressekonto.test",
|
||||
"presseecho.test",
|
||||
"assets.presseecho.test",
|
||||
"businessportal24.test",
|
||||
"assets.businessportal24.test",
|
||||
"localhost",
|
||||
"0.0.0.0",
|
||||
];
|
||||
|
||||
/**
|
||||
* Whitelist-basiertes Watch-Filtering für Vite/Chokidar.
|
||||
*
|
||||
* @param {string[]} allowedDirs Pfade relativ zum Projektroot
|
||||
*/
|
||||
export function createWatchIgnored(allowedDirs) {
|
||||
const allowedPrefixes = allowedDirs.map((dir) =>
|
||||
path.join(projectRoot, dir),
|
||||
);
|
||||
|
||||
return (filePath) => {
|
||||
const normalized = path.normalize(filePath);
|
||||
|
||||
if (normalized.includes(`${path.sep}node_modules${path.sep}`)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (normalized === projectRoot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isAllowed = allowedPrefixes.some(
|
||||
(prefix) =>
|
||||
normalized === prefix ||
|
||||
normalized.startsWith(prefix + path.sep),
|
||||
);
|
||||
|
||||
if (isAllowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isAncestorOfAllowed = allowedPrefixes.some((prefix) =>
|
||||
prefix.startsWith(normalized + path.sep),
|
||||
);
|
||||
|
||||
return !isAncestorOfAllowed;
|
||||
};
|
||||
}
|
||||
|
||||
/** Backend/Admin: FluxUI, Livewire-Panel, Customer-Bereich */
|
||||
export const portalWatchDirs = [
|
||||
"resources/js",
|
||||
"resources/css",
|
||||
"resources/views/livewire/admin",
|
||||
"resources/views/livewire/customer",
|
||||
"resources/views/livewire/components",
|
||||
"resources/views/livewire/settings",
|
||||
"resources/views/livewire/auth",
|
||||
"resources/views/layouts",
|
||||
"resources/views/components/layouts",
|
||||
"resources/views/components/portal",
|
||||
"resources/views/components/settings",
|
||||
"resources/views/partials",
|
||||
"resources/views/admin",
|
||||
"app/Livewire",
|
||||
"vendor/livewire/flux/dist",
|
||||
"vendor/livewire/flux/stubs",
|
||||
"vendor/livewire/flux-pro/stubs",
|
||||
"vendor/laravel/framework/src/Illuminate/Pagination/resources/views",
|
||||
];
|
||||
|
||||
/** Öffentliche Frontends: pressekonto Hub, presseecho, businessportal24 */
|
||||
export const webWatchDirs = [
|
||||
"resources/js",
|
||||
"resources/css",
|
||||
"resources/views/web",
|
||||
"resources/views/livewire/web",
|
||||
"resources/views/components/web",
|
||||
];
|
||||
|
||||
export const portalRefreshPaths = [
|
||||
"resources/views/livewire/admin/**",
|
||||
"resources/views/livewire/customer/**",
|
||||
"resources/views/livewire/components/**",
|
||||
"resources/views/livewire/settings/**",
|
||||
"resources/views/livewire/auth/**",
|
||||
"resources/views/layouts/**",
|
||||
"resources/views/components/layouts/**",
|
||||
"resources/views/components/portal/**",
|
||||
"resources/views/partials/**",
|
||||
"resources/views/admin/**",
|
||||
"app/Livewire/**",
|
||||
];
|
||||
|
||||
export const webRefreshPaths = [
|
||||
"resources/views/web/**",
|
||||
"resources/views/livewire/web/**",
|
||||
"resources/views/components/web/**",
|
||||
];
|
||||
|
|
@ -1,63 +1,62 @@
|
|||
/**
|
||||
* Vite-Konfiguration für öffentliche Frontends (Web)
|
||||
* - Domains: pressekonto.test (Hub), presseecho.test, businessportal24.test
|
||||
* - Asset-Subdomains: assets.pressekonto.test, assets.presseecho.test, assets.businessportal24.test
|
||||
* - Port: 5178
|
||||
* - Build: public/build/web
|
||||
*
|
||||
* Backend/Admin auf pressekonto.test → vite.portal.config.js (dev:portal)
|
||||
*
|
||||
* Starten mit: npm run dev:web
|
||||
*/
|
||||
import { defineConfig } from "vite";
|
||||
import laravel from "laravel-vite-plugin";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
|
||||
// SSL-Konfiguration - für Entwicklung ohne echte Zertifikate
|
||||
const httpsConfig =
|
||||
process.env.NODE_ENV === "production"
|
||||
? {
|
||||
// In Produktion: echte Zertifikate verwenden
|
||||
key: process.env.SSL_KEY_PATH,
|
||||
cert: process.env.SSL_CERT_PATH,
|
||||
}
|
||||
: true; // Self-signed für Entwicklung
|
||||
import {
|
||||
WEB_ALLOWED_HOSTS,
|
||||
WEB_CORS_ORIGINS,
|
||||
WEB_HOT_FILE,
|
||||
WEB_PORT,
|
||||
createWatchIgnored,
|
||||
webRefreshPaths,
|
||||
webWatchDirs,
|
||||
} from "./vite.shared.js";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
laravel({
|
||||
input: [
|
||||
// Web Theme CSS Dateien
|
||||
"resources/css/web/theme-businessportal24.css",
|
||||
"resources/css/web/theme-presseecho.css", // Neu: CSS für presseecho hinzugefügt, um beide Themes vorab zu kompilieren
|
||||
"resources/css/web/theme-pressekonto.css", // Hub-Landing pressekonto.de
|
||||
"resources/css/web/theme-presseecho.css",
|
||||
"resources/css/web/theme-pressekonto.css",
|
||||
"resources/js/app.js",
|
||||
],
|
||||
refresh: ["resources/views/web/**/*.blade.php"],
|
||||
refresh: webRefreshPaths,
|
||||
hotFile: WEB_HOT_FILE,
|
||||
}),
|
||||
tailwindcss({
|
||||
config: "./tailwind.web.config.js",
|
||||
}),
|
||||
],
|
||||
server: {
|
||||
https: false, // Traefik übernimmt SSL, Vite läuft intern auf HTTP
|
||||
https: false,
|
||||
watch: {
|
||||
ignored: createWatchIgnored(webWatchDirs),
|
||||
},
|
||||
cors: {
|
||||
origin: [
|
||||
"https://businessportal24.test",
|
||||
"https://assets.businessportal24.test",
|
||||
],
|
||||
origin: WEB_CORS_ORIGINS,
|
||||
credentials: true,
|
||||
},
|
||||
host: "0.0.0.0",
|
||||
port: 5178, // Web-spezifischer Port
|
||||
port: WEB_PORT,
|
||||
strictPort: true,
|
||||
allowedHosts: [
|
||||
"assets.businessportal24.test",
|
||||
"businessportal24.test",
|
||||
"assets.presseecho.test", // Neu: presseecho-Host hinzugefügt
|
||||
"presseecho.test", // Neu: presseecho-Host hinzugefügt
|
||||
"assets.pressekonto.test", // Hub-Landing pressekonto.de
|
||||
"pressekonto.test", // Hub-Landing pressekonto.de
|
||||
"localhost",
|
||||
"0.0.0.0",
|
||||
],
|
||||
allowedHosts: WEB_ALLOWED_HOSTS,
|
||||
hmr: {
|
||||
host: "assets.businessportal24.test",
|
||||
protocol: "wss",
|
||||
},
|
||||
origin: "https://assets.businessportal24.test", // Ohne Port!
|
||||
},
|
||||
build: {
|
||||
outDir: `public/build/web`,
|
||||
outDir: "public/build/web",
|
||||
assetsDir: "",
|
||||
manifest: "manifest.json",
|
||||
rollupOptions: {
|
||||
|
|
@ -66,4 +65,4 @@ export default defineConfig({
|
|||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue