From a666a43690504570932fe4adb5280d6a80f32d86 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Apr 2026 13:03:44 +0000 Subject: [PATCH 1/3] Configure Identity cookie paths to match ViewPrefix ASP.NET Identity defaults the login redirect to /Account/Login, but the Users module's ViewPrefix is /Identity/Account, placing the actual login route at /Identity/Account/Login. This mismatch caused unauthenticated requests to redirect to a 404 page. Explicitly sets LoginPath, LogoutPath, and AccessDeniedPath on the application cookie to match the module's ViewPrefix. --- modules/Users/src/SimpleModule.Users/UsersModule.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/Users/src/SimpleModule.Users/UsersModule.cs b/modules/Users/src/SimpleModule.Users/UsersModule.cs index 1190b9c4..011dfca3 100644 --- a/modules/Users/src/SimpleModule.Users/UsersModule.cs +++ b/modules/Users/src/SimpleModule.Users/UsersModule.cs @@ -24,6 +24,13 @@ public void ConfigureServices(IServiceCollection services, IConfiguration config .AddEntityFrameworkStores() .AddDefaultTokenProviders(); + services.ConfigureApplicationCookie(options => + { + options.LoginPath = "/Identity/Account/Login"; + options.LogoutPath = "/Identity/Account/Logout"; + options.AccessDeniedPath = "/Identity/Account/AccessDenied"; + }); + // Bridge UsersModuleOptions into ASP.NET Identity options services.AddSingleton, ApplyUsersModuleOptions>(); From dd889019b8f8cfec2606c8a3c298abbd76e4b167 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Apr 2026 14:33:37 +0000 Subject: [PATCH 2/3] Enable .mjs static file fallback in all environments The UseStaticFiles fallback for .mjs chunks was gated behind IsDevelopment(), but MapStaticAssets can miss .mjs files when the Docker image's static asset manifest is stale. This caused 404s for Users module page chunks in production while other modules worked. Removing the environment gate ensures .mjs files in wwwroot are always served even if MapStaticAssets doesn't know about them. --- .../SimpleModule.Hosting/SimpleModuleHostExtensions.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/framework/SimpleModule.Hosting/SimpleModuleHostExtensions.cs b/framework/SimpleModule.Hosting/SimpleModuleHostExtensions.cs index b6eaa933..5aef2348 100644 --- a/framework/SimpleModule.Hosting/SimpleModuleHostExtensions.cs +++ b/framework/SimpleModule.Hosting/SimpleModuleHostExtensions.cs @@ -188,10 +188,9 @@ public static async Task UseSimpleModuleInfrastructure(this WebApplication app) UseStaticFileCaching(app); app.MapStaticAssets(); - // Fallback for dynamically generated .mjs chunks (Vite watch rebuilds) - // MapStaticAssets only knows about files present at build time; - // Vite generates new hash-named chunks at runtime that need correct MIME types - if (app.Environment.IsDevelopment()) + // Fallback for .mjs chunks not in the MapStaticAssets manifest. + // MapStaticAssets only knows about files present at publish time; + // mismatched builds or Vite watch rebuilds can produce chunks it misses. { var provider = new FileExtensionContentTypeProvider(); provider.Mappings[".mjs"] = "application/javascript"; From ae95ad028a542483520930323a36c8b1d5706176 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Apr 2026 14:39:05 +0000 Subject: [PATCH 3/3] Show error toast when page resolution fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a module's page chunk fails to load (404, network error, missing page registry entry), the user previously saw nothing — the page just stayed blank. Now shows a toast: "Failed to load page X. Try refreshing the page." --- template/SimpleModule.Host/ClientApp/app.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/template/SimpleModule.Host/ClientApp/app.tsx b/template/SimpleModule.Host/ClientApp/app.tsx index 48051a40..48f58be3 100644 --- a/template/SimpleModule.Host/ClientApp/app.tsx +++ b/template/SimpleModule.Host/ClientApp/app.tsx @@ -135,8 +135,13 @@ function showErrorToast(message: string) { createInertiaApp({ resolve: async (name) => { - const page = await resolvePage(name); - return resolveLayout(page); + try { + const page = await resolvePage(name); + return resolveLayout(page); + } catch (err) { + showErrorToast(`Failed to load page "${name}". Try refreshing the page.`); + throw err; + } }, setup({ el, App, props }) { createRoot(el).render();