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:
Kevin Adametz 2026-06-12 08:16:09 +00:00
parent 4bb9094207
commit 0efabaf446
15 changed files with 485 additions and 109 deletions

View file

@ -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));
}
}

View file

@ -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]);
}

View 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));
}
}

View file

@ -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'), //

View file

View file

@ -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`)"

View file

@ -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\"",

View file

@ -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>

View file

@ -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;
};

View file

@ -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",

View 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();
});

View file

@ -0,0 +1,7 @@
<?php
test('example', function () {
$response = $this->get('/');
$response->assertStatus(200);
});

View file

@ -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
View 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/**",
];

View file

@ -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({
},
},
},
});
});