From 38857038a0e337cc11f215506c774226bae16b95 Mon Sep 17 00:00:00 2001 From: Sluijsens Date: Sun, 26 Jul 2026 21:01:28 +0200 Subject: [PATCH] Serves public website, admin SPA, and API from a single host Shared hosting (e.g. mijnhostingpartner.nl) typically allows only one site/app-pool, so SlpModularCms.Api now serves everything itself: '/' for the customer's public website (deployed separately, not part of this repo), '/admin' for the CMS admin SPA, and '/api/v1' for the API as before. - Program.cs: static files from wwwroot + SPA fallbacks per path so client-side routing works for both frontends. - frontend/: builds with base '/admin/' in production (dev unchanged), router basepath follows suit. - SlpModularCms.Api.csproj: publish now builds the admin frontend and copies its output into wwwroot/admin automatically. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Wi5qHAuq8UbzN4NLUFeKkJ --- .gitignore | 4 ++-- README.md | 16 +++++++++++++++- frontend/src/router.tsx | 2 ++ frontend/vite.config.ts | 6 ++++-- src/SlpModularCms.Api/Program.cs | 11 +++++++++++ .../SlpModularCms.Api.csproj | 19 +++++++++++++++++++ 6 files changed, 53 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index d83e540..a7f4641 100644 --- a/.gitignore +++ b/.gitignore @@ -28,8 +28,8 @@ bld/ # Visual Studio 2015/2017/2019/2022 cache/options directory .vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ +# Generated on publish: admin SPA build (+ separately-deployed public website), see SlpModularCms.Api.csproj +wwwroot/ # Visual Studio Code design time settings .vscode/ diff --git a/README.md b/README.md index 2eb3c9e..337c4c0 100644 --- a/README.md +++ b/README.md @@ -281,11 +281,25 @@ Zie `aidlc-docs/features/local-dev-master-slave-setup/inception/requirements/req ## Productie Setup +### 0. Routing op de webhost +Op een shared-hosting omgeving (zoals mijnhostingpartner.nl) is er meestal maar ruimte voor 1 website/app-pool. Daarom serveert `SlpModularCms.Api` alles zelf, vanuit één proces: + +| Pad | Inhoud | +|---|---| +| `/` | De publieke website van de klant — **geen onderdeel van deze repo**, wordt los aangeleverd/gedeployed in `wwwroot/` | +| `/admin` | De CMS admin-UI (`frontend/`), gebouwd met `base: '/admin/'` en automatisch gekopieerd naar `wwwroot/admin/` bij `dotnet publish` | +| `/api/v1/...` | Deze API | + +Beide SPA's krijgen een fallback naar hun eigen `index.html` zodat client-side routes (bijv. `/admin/dashboard`) werken; ontbrekende bestanden (met een extensie, bijv. `/admin/assets/x.js`) blijven gewoon 404'en. Zie `Program.cs` (`MapFallbackToFile`) en `SlpModularCms.Api.csproj` (`BuildAndCopyAdminFrontend`-target). + +Voor lokale ontwikkeling verandert er niets: `pnpm dev` blijft op `http://localhost:5173` draaien zonder `/admin`-prefix. + ### 1. Build & Publish -Compileer de applicatie voor productie: +Compileer de applicatie voor productie — dit bouwt en kopieert automatisch ook de admin-frontend naar `wwwroot/admin/`: ```powershell dotnet publish src/SlpModularCms.Api -c Release -o ./publish ``` +Kopieer daarna de publieke website van de klant naar `./publish/wwwroot/` (alles behalve de `admin/`-submap, die blijft ongemoeid). ### 2. Runtime Configuratie In productie moeten gevoelige instellingen worden doorgegeven via Environment Variables: diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index 6d869cf..85b9a00 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -231,6 +231,8 @@ export const routeTree = rootRoute.addChildren([ export const router = createRouter({ routeTree, defaultPreload: 'intent', + // Matches vite.config.ts's `base`: '/admin/' in production builds, '/' in dev. + basepath: import.meta.env.BASE_URL, context: { auth: undefined as unknown as AuthContextValue }, }); diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 35b2198..6f70454 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -5,7 +5,9 @@ import react from '@vitejs/plugin-react'; import tailwindcss from '@tailwindcss/vite'; // https://vite.dev/config/ -export default defineConfig({ +export default defineConfig(({ command }) => ({ + // Production builds are deployed under /admin (see Program.cs); the dev server keeps serving from '/'. + base: command === 'build' ? '/admin/' : '/', plugins: [react(), tailwindcss()], resolve: { alias: { @@ -34,4 +36,4 @@ export default defineConfig({ ], }, }, -}); +})); diff --git a/src/SlpModularCms.Api/Program.cs b/src/SlpModularCms.Api/Program.cs index ba5fa60..582ff69 100644 --- a/src/SlpModularCms.Api/Program.cs +++ b/src/SlpModularCms.Api/Program.cs @@ -46,6 +46,12 @@ if (app.Environment.IsDevelopment()) app.UseHttpsRedirection(); +// Serve the public website ('/') and the CMS admin SPA ('/admin') from wwwroot. +// wwwroot/index.html + assets -> public website (built and deployed separately, not part of this repo) +// wwwroot/admin/index.html + assets -> CMS admin build (see frontend/, copied in on publish) +app.UseDefaultFiles(); +app.UseStaticFiles(); + app.UseCors(); // 7. Use Module Middleware @@ -56,4 +62,9 @@ app.UseAuthorization(); app.MapControllers(); +// SPA fallbacks so client-side routes (e.g. /admin/dashboard) resolve to the right index.html +// instead of 404ing. The "nonfile" constraint keeps genuinely missing assets (e.g. /admin/assets/x.js) as 404s. +app.MapFallbackToFile("/admin/{*path:nonfile}", "admin/index.html"); +app.MapFallbackToFile("{*path:nonfile}", "index.html"); + app.Run(); diff --git a/src/SlpModularCms.Api/SlpModularCms.Api.csproj b/src/SlpModularCms.Api/SlpModularCms.Api.csproj index d6b4d80..3f4e12c 100644 --- a/src/SlpModularCms.Api/SlpModularCms.Api.csproj +++ b/src/SlpModularCms.Api/SlpModularCms.Api.csproj @@ -24,4 +24,23 @@ + + + $(MSBuildProjectDirectory)/../../frontend + + + + + + + + + + + +