Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions Web/Resgrid.Web/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Reflection;
Expand All @@ -26,6 +27,7 @@
using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Resgrid.Config;
using Resgrid.Framework;
Expand Down Expand Up @@ -375,10 +377,6 @@ public void ConfigureServices(IServiceCollection services)
pipeline.AddCssBundle("/css/pub-bundle.css",
"css/style.css", "css/animate.css", "lib/font-awesome/css/font-awesome.min.css");

// Angular App code
pipeline.AddJavaScriptBundle("/js/ng/app.js",
"js/ng/vendor.js", "js/ng/runtime.js", "js/ng/polyfills.js", "js/ng/main.js");

// Internal app style bundle
pipeline.AddCssBundle("/css/int-bundle.css",
"lib/font-awesome/css/font-awesome.min.css", "lib/metisMenu/dist/metisMenu.min.css", "lib/bootstrap-tour/build/css/bootstrap-tour.min.css",
Expand Down Expand Up @@ -586,6 +584,26 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF

app.UseCookiePolicy(cookiePolicyOptions);

// Serve the Vite ESM assets directly so legacy optimization doesn't rewrite module chunks.
app.UseWhen(
context => context.Request.Path.StartsWithSegments("/js/ng"),
clientApp =>
{
clientApp.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(env.WebRootPath, "js", "ng")),
RequestPath = "/js/ng",
OnPrepareResponse = context =>
{
var cacheControl = context.Context.Request.Path.StartsWithSegments("/js/ng/chunks")
? "public,max-age=31536000,immutable,no-transform"
: "no-cache,no-transform";

context.Context.Response.Headers["Cache-Control"] = cacheControl;
}
Comment on lines +596 to +603
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hashed /js/ng/assets/* files are missing the immutable cache policy.

The vite.config.ts referenced in context emits three output groups, but only one of them is being treated as immutable here:

  • react-elements.js — entry, not hashed → no-cache ✅ correct
  • chunks/[name]-[hash].js — hashed → immutable ✅ correct
  • assets/[name]-[hash][extname] — hashed → currently no-cache

/js/ng/assets/* is content-hashed just like chunks/*, so it's equally safe for long-term immutable caching. As written, browsers will revalidate those fingerprinted assets on every navigation, which negates the hashing strategy.

♻️ Suggested tweak
 OnPrepareResponse = context =>
 {
-	var cacheControl = context.Context.Request.Path.StartsWithSegments("/js/ng/chunks")
+	var path = context.Context.Request.Path;
+	var isHashed = path.StartsWithSegments("/js/ng/chunks")
+		|| path.StartsWithSegments("/js/ng/assets");
+	var cacheControl = isHashed
 		? "public,max-age=31536000,immutable,no-transform"
 		: "no-cache,no-transform";

 	context.Context.Response.Headers["Cache-Control"] = cacheControl;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
OnPrepareResponse = context =>
{
var cacheControl = context.Context.Request.Path.StartsWithSegments("/js/ng/chunks")
? "public,max-age=31536000,immutable,no-transform"
: "no-cache,no-transform";
context.Context.Response.Headers["Cache-Control"] = cacheControl;
}
OnPrepareResponse = context =>
{
var path = context.Context.Request.Path;
var isHashed = path.StartsWithSegments("/js/ng/chunks")
|| path.StartsWithSegments("/js/ng/assets");
var cacheControl = isHashed
? "public,max-age=31536000,immutable,no-transform"
: "no-cache,no-transform";
context.Context.Response.Headers["Cache-Control"] = cacheControl;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Startup.cs` around lines 596 - 603, The cache policy
currently marks only "/js/ng/chunks" as immutable; update the OnPrepareResponse
logic to also treat "/js/ng/assets" as immutable by checking both path segments
(e.g., include "/js/ng/assets" alongside "/js/ng/chunks" when computing
cacheControl) so fingerprinted assets under assets/[name]-[hash][extname] get
"public,max-age=31536000,immutable,no-transform" while non-hashed entries like
react-elements.js remain "no-cache,no-transform"; modify the conditional in the
OnPrepareResponse delegate accordingly.

});
});
Comment on lines +592 to +605
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm whether the Vite output directory is expected to always exist at Configure-time
# (e.g. checked in, produced by an msbuild target, or a pre-run step).
fd -t d -u '^ng$' | rg -n 'wwwroot/js/ng'
rg -nP --type=xml -C2 '(js[/\\\\]ng|vite|npm (run )?build)' -g '*.csproj'
rg -nP -C2 '(js[/\\\\]ng|vite\s+build|npm\s+run\s+build)' -g '!node_modules' -g '!*.min.*' -g '!*.map'

Repository: Resgrid/Core

Length of output: 6521


🏁 Script executed:

#!/bin/bash

# Check if wwwroot/js/ng exists in git (not gitignored)
git ls-files | grep -E 'wwwroot/js/ng'

# Check .gitignore patterns that might exclude this directory
cat .gitignore 2>/dev/null | grep -E '(wwwroot|js|ng|/dist)' || echo "No gitignore patterns found matching"

# Check if wwwroot itself is gitignored
git check-ignore -v 'Web/Resgrid.Web/wwwroot' 2>/dev/null || echo "wwwroot is not gitignored"
git check-ignore -v 'Web/Resgrid.Web/wwwroot/js/ng' 2>/dev/null || echo "wwwroot/js/ng is not gitignored"

# List what's actually in the directory in git
echo "=== Files in wwwroot/js/ng in git ==="
git ls-files | grep 'wwwroot/js/ng' | head -20 || echo "No files found in git"

Repository: Resgrid/Core

Length of output: 2726


Cache-Control headers should apply immutable to all content-hashed assets, not just chunks.

Per vite.config.ts, both /js/ng/chunks/ and /js/ng/assets/ output are content-hashed ([name]-[hash] pattern), making them safe for long-lived caching. Currently only chunks receive immutable, max-age=31536000; assets still get no-cache, which undermines the caching benefit of the hash. The unhashed entry react-elements.js correctly stays at no-cache.

Update the cache-control logic to treat all hashed assets uniformly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Startup.cs` around lines 592 - 605, The Cache-Control
decision in the OnPrepareResponse lambda for clientApp.UseStaticFiles only marks
"/js/ng/chunks" as immutable; update the conditional in that lambda (the
cacheControl assignment using context.Context.Request.Path.StartsWithSegments)
to also treat "/js/ng/assets" as long-lived (e.g., use
StartsWithSegments("/js/ng/chunks") || StartsWithSegments("/js/ng/assets")) so
both content-hashed chunks and assets get
"public,max-age=31536000,immutable,no-transform" while leaving unhashed entries
(like react-elements.js) using "no-cache,no-transform".


app.UseWebOptimizer();
app.UseStaticFiles();
app.UseRouting();
Expand Down
Loading