@@ -431,44 +431,6 @@ void RegisterDbContext<TContext>(string connectionStringKey) where TContext : Db
431431
432432 }
433433
434- // In development, set up Vite proxy BEFORE rewrite rules so it can handle .ts/.js files
435- if ( app . Environment . IsDevelopment ( ) )
436- {
437- // Development: Proxy Vue.js assets to Vite dev server for hot module replacement (HMR)
438- // This middleware intercepts requests for Vue assets and forwards them to the Vite dev server
439- app . Use ( async ( context , next ) =>
440- {
441- if ( ViteProxyHelpers . ShouldProxyToVite ( context , VueAppNames ) )
442- {
443- try
444- {
445- // Use the registered HttpClient from dependency injection
446- var httpClientFactory = context . RequestServices . GetRequiredService < IHttpClientFactory > ( ) ;
447- var httpClient = httpClientFactory . CreateClient ( "ViteProxy" ) ;
448-
449- // Build the Vite server URL and try to proxy directly
450- var viteUrl = ViteProxyHelpers . BuildViteUrl ( context . Request . Path , context . Request . QueryString , VueAppNames ) ;
451- var requestMessage = ViteProxyHelpers . CreateProxyRequest ( context , viteUrl ) ;
452- using var response = await httpClient . SendAsync ( requestMessage , HttpCompletionOption . ResponseHeadersRead , context . RequestAborted ) ;
453-
454- // Copy the response back to the client
455- await ViteProxyHelpers . CopyProxyResponse ( context , response ) ;
456- return ; // Successfully proxied, don't continue to static files
457- }
458- catch ( Exception ex )
459- {
460- var logger = context . RequestServices . GetRequiredService < ILogger < Program > > ( ) ;
461- logger . LogDebug ( ex , "Vite server not available, falling back to static files for {Path}" ,
462- Uri . EscapeDataString ( context . Request . Path . Value ?? "unknown" ) ) ;
463- // Fall through to static file serving
464- }
465- }
466-
467- // Continue to static file serving (either Vite not needed or not available)
468- await next ( ) ;
469- } ) ;
470- }
471-
472434 var rewriteOptions = new RewriteOptions ( ) ;
473435
474436 // Add redirects and rewrites for each SPA using centralized app names
@@ -485,41 +447,78 @@ void RegisterDbContext<TContext>(string connectionStringKey) where TContext : Db
485447 rewriteOptions . AddRewrite ( $@ "(?i)^{ escapedAppName } ", $ "/2/vue/src/{ lowerAppName } /index.html", true ) ;
486448 }
487449
488- app . UseRewriter ( rewriteOptions ) ;
489-
490- //for the vue src files, use directories in the url but serve index.html
450+ // Default-file convention for /vue (legacy path).
491451 app . UseDefaultFiles ( new DefaultFilesOptions
492452 {
493453 DefaultFileNames = new List < string > { "index.html" } ,
494454 FileProvider = new PhysicalFileProvider (
495- Path . Join ( builder . Environment . ContentRootPath , "wwwroot/ vue" ) ) ,
455+ Path . Join ( builder . Environment . ContentRootPath , "wwwroot" , " vue") ) ,
496456 RequestPath = "/vue" ,
497457 RedirectToAppendTrailingSlash = true
498458 } ) ;
499459
500- // Static file serving configuration
501- // Serve built Vue files - in development proxy middleware runs first,
502- // in production these files are served directly
503- app . UseStaticFiles ( new StaticFileOptions
504- {
505- FileProvider = new PhysicalFileProvider (
506- Path . Join ( builder . Environment . ContentRootPath , "wwwroot/vue" ) ) ,
507- RequestPath = "/2/vue"
508- } ) ;
509-
510- // Serve other static files
460+ // General static files (favicon, /css, /js, /images, etc.).
511461 app . UseStaticFiles ( ) ;
512462
513- // Add sitemap middleware after static file handling
514463 app . UseSitemapMiddleware ( ) ;
515464
516- // apply settings define earlier
465+ // Routing first so subsequent middleware can defer to a matched MVC endpoint.
517466 app . UseRouting ( ) ;
518467 app . UseAuthentication ( ) ;
519468 app . UseAuthorization ( ) ;
520469 app . UseCookiePolicy ( ) ;
521470 app . UseSession ( ) ;
522471
472+ // SPA shell serving — Vue app prefixes like /CMS, /Effort, etc.
473+ // Only runs when no MVC controller endpoint claimed the path, so attribute-routed
474+ // legacy endpoints (e.g. /CMS/Files → CMSController.Files) reach the controller
475+ // instead of being rewritten to the SPA shell.
476+ app . UseWhen (
477+ ctx => ctx . GetEndpoint ( ) is null ,
478+ branch =>
479+ {
480+ if ( app . Environment . IsDevelopment ( ) )
481+ {
482+ // Dev: proxy Vue assets and SPA routes to the Vite dev server (HMR).
483+ branch . Use ( async ( context , next ) =>
484+ {
485+ if ( ViteProxyHelpers . ShouldProxyToVite ( context , VueAppNames ) )
486+ {
487+ try
488+ {
489+ var httpClientFactory = context . RequestServices . GetRequiredService < IHttpClientFactory > ( ) ;
490+ var httpClient = httpClientFactory . CreateClient ( "ViteProxy" ) ;
491+
492+ var viteUrl = ViteProxyHelpers . BuildViteUrl ( context . Request . Path , context . Request . QueryString , VueAppNames ) ;
493+ var requestMessage = ViteProxyHelpers . CreateProxyRequest ( context , viteUrl ) ;
494+ using var response = await httpClient . SendAsync ( requestMessage , HttpCompletionOption . ResponseHeadersRead , context . RequestAborted ) ;
495+
496+ await ViteProxyHelpers . CopyProxyResponse ( context , response ) ;
497+ return ;
498+ }
499+ catch ( Exception ex ) when ( ex is HttpRequestException or TaskCanceledException )
500+ {
501+ var viteLogger = context . RequestServices . GetRequiredService < ILogger < Program > > ( ) ;
502+ viteLogger . LogDebug ( ex , "Vite server not available, falling back to static files for {Path}" ,
503+ Uri . EscapeDataString ( context . Request . Path . Value ?? "unknown" ) ) ;
504+ }
505+ }
506+
507+ await next ( ) ;
508+ } ) ;
509+ }
510+
511+ // Prod (and dev fallback): rewrite SPA routes to the built SPA shell,
512+ // then serve the static file from wwwroot/vue.
513+ branch . UseRewriter ( rewriteOptions ) ;
514+ branch . UseStaticFiles ( new StaticFileOptions
515+ {
516+ FileProvider = new PhysicalFileProvider (
517+ Path . Join ( builder . Environment . ContentRootPath , "wwwroot" , "vue" ) ) ,
518+ RequestPath = "/2/vue"
519+ } ) ;
520+ } ) ;
521+
523522 // All health-check pipeline wiring lives in HealthCheckExtensions.
524523 app . UseViperHealthChecks ( ) ;
525524
0 commit comments