From 37dd66a83bf87120a80697d843bd6715fe0559cc Mon Sep 17 00:00:00 2001 From: DonaldDWebster Date: Sat, 5 Oct 2024 13:35:15 -0500 Subject: [PATCH] Replace ScriptsMiddleware(#15629) --- OrchardCore.sln | 3 + .../Endpoints/Api/InitSdkEndpoint.cs | 56 ++++++++++++++ .../Endpoints/Api/LoadSdkEndPoint.cs | 61 +++++++++++++++ .../OrchardCore.Facebook/ScriptsMiddleware.cs | 63 ---------------- .../OrchardCore.Facebook/Startup.cs | 3 +- .../Endpoints/Api/GetIntellisenseEndpoint.cs | 74 +++++++++++++++++++ .../OrchardCore.Liquid/ScriptsMiddleware.cs | 70 ------------------ .../OrchardCore.Liquid/Startup.cs | 4 +- 8 files changed, 199 insertions(+), 135 deletions(-) create mode 100644 src/OrchardCore.Modules/OrchardCore.Facebook/Endpoints/Api/InitSdkEndpoint.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.Facebook/Endpoints/Api/LoadSdkEndPoint.cs delete mode 100644 src/OrchardCore.Modules/OrchardCore.Facebook/ScriptsMiddleware.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.Liquid/Endpoints/Api/GetIntellisenseEndpoint.cs delete mode 100644 src/OrchardCore.Modules/OrchardCore.Liquid/ScriptsMiddleware.cs diff --git a/OrchardCore.sln b/OrchardCore.sln index 172cbd5cfc9..60fd88b1fc0 100644 --- a/OrchardCore.sln +++ b/OrchardCore.sln @@ -527,6 +527,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Queries.Core", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Sms.Azure", "src\OrchardCore.Modules\OrchardCore.Sms.Azure\OrchardCore.Sms.Azure.csproj", "{013C8BBF-6879-4B47-80C9-A466923E45E5}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Endpoints", "Endpoints", "{4B655F8B-66A6-44DC-BB6A-C84937BF5163}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1636,6 +1638,7 @@ Global {4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7} = {F23AC6C2-DE44-4699-999D-3C478EF3D691} {61B358F2-702C-40AA-9DF7-7121248FE6DE} = {F23AC6C2-DE44-4699-999D-3C478EF3D691} {013C8BBF-6879-4B47-80C9-A466923E45E5} = {A066395F-6F73-45DC-B5A6-B4E306110DCE} + {4B655F8B-66A6-44DC-BB6A-C84937BF5163} = {A066395F-6F73-45DC-B5A6-B4E306110DCE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {46A1D25A-78D1-4476-9CBF-25B75E296341} diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Endpoints/Api/InitSdkEndpoint.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Endpoints/Api/InitSdkEndpoint.cs new file mode 100644 index 00000000000..d4563fcf65c --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Endpoints/Api/InitSdkEndpoint.cs @@ -0,0 +1,56 @@ +using System.Globalization; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using OrchardCore.ContentManagement; +using OrchardCore.Settings; +using OrchardCore.Facebook.Settings; + +namespace OrchardCore.Facebook; +internal static class InitSdkEndpoint +{ + public static IEndpointRouteBuilder AddInitSdkEndpoint(this IEndpointRouteBuilder builder) + { + builder.MapGet("orchardcore/facebook/sdk/fbsdk.js", HandleAsync) + .AllowAnonymous() + .DisableAntiforgery(); + + return builder; + } + + [Authorize(AuthenticationSchemes = "Api")] + private static async Task HandleAsync( + ContentItem model, + ISiteService siteService, + HttpContext httpContext, + bool draft = false) + { + ArgumentNullException.ThrowIfNull(httpContext); + + var script = await getInitSdkScript(siteService); + + if (script == null) + { + return Results.NotFound(); + } + + return Results.Ok(script); + } + + private static async Task getInitSdkScript(ISiteService siteService) + { + var settings = await siteService.GetSettingsAsync(); + var locale = CultureInfo.CurrentUICulture.Name.Replace('-', '_'); + + var script = $@"(function(d){{ + var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {{ return; }} + js = d.createElement('script'); js.id = id; js.async = true; + js.src = ""https://connect.facebook.net/{locale}/{settings.SdkJs}""; + d.getElementsByTagName('head')[0].appendChild(js); + }} (document));"; + + return script; + } +} + diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Endpoints/Api/LoadSdkEndPoint.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Endpoints/Api/LoadSdkEndPoint.cs new file mode 100644 index 00000000000..ddcec9eebc4 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Endpoints/Api/LoadSdkEndPoint.cs @@ -0,0 +1,61 @@ +using System.Globalization; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using OrchardCore.ContentManagement; +using OrchardCore.Settings; +using OrchardCore.Facebook.Settings; + +namespace OrchardCore.Facebook; +internal static class LoadSdkEndpoint +{ + public static IEndpointRouteBuilder AddLoadSdkEndpoint(this IEndpointRouteBuilder builder) + { + builder.MapGet("orchardcore/facebook/sdk/fb.js", HandleAsync) + .AllowAnonymous() + .DisableAntiforgery(); + + return builder; + } + + [Authorize(AuthenticationSchemes = "Api")] + private static async Task HandleAsync( + ContentItem model, + ISiteService siteService, + HttpContext httpContext, + bool draft = false) + { + ArgumentNullException.ThrowIfNull(httpContext); + + var script = await getLoadSdkScript(siteService); + + if (script == null) + { + return Results.NotFound(); + } + + return Results.Ok(script); + } + + private static async Task getLoadSdkScript(ISiteService siteService) + { + var settings = await siteService.GetSettingsAsync(); + var locale = CultureInfo.CurrentUICulture.Name.Replace('-', '_'); + + var script = default(string); + + if (!string.IsNullOrWhiteSpace(settings?.AppId)) + { + var options = $"{{ appId:'{settings.AppId}',version:'{settings.Version}'"; + options = string.IsNullOrWhiteSpace(settings.FBInitParams) + ? string.Concat(options, "}") + : string.Concat(options, ",", settings.FBInitParams, "}"); + + script = $"window.fbAsyncInit = function(){{ FB.init({options});}};"; + } + + return script; + } +} + diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/ScriptsMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/ScriptsMiddleware.cs deleted file mode 100644 index d0ff92f0b48..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/ScriptsMiddleware.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Globalization; -using System.Text; -using Microsoft.AspNetCore.Http; -using OrchardCore.Facebook.Settings; -using OrchardCore.Settings; - -namespace OrchardCore.Facebook; - -public class ScriptsMiddleware -{ - private readonly RequestDelegate _next; - private readonly ISiteService _siteService; - - public ScriptsMiddleware(RequestDelegate next, ISiteService siteService) - { - _next = next; - _siteService = siteService; - } - - public async Task Invoke(HttpContext httpContext) - { - ArgumentNullException.ThrowIfNull(httpContext); - - if (httpContext.Request.Path.StartsWithSegments("/OrchardCore.Facebook/sdk", StringComparison.OrdinalIgnoreCase)) - { - var script = default(string); - var settings = await _siteService.GetSettingsAsync(); - - if (Path.GetFileName(httpContext.Request.Path.Value) == "fbsdk.js") - { - var locale = CultureInfo.CurrentUICulture.Name.Replace('-', '_'); - script = $@"(function(d){{ - var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {{ return; }} - js = d.createElement('script'); js.id = id; js.async = true; - js.src = ""https://connect.facebook.net/{locale}/{settings.SdkJs}""; - d.getElementsByTagName('head')[0].appendChild(js); - }} (document));"; - } - else if (Path.GetFileName(httpContext.Request.Path.Value) == "fb.js") - { - if (!string.IsNullOrWhiteSpace(settings?.AppId)) - { - var options = $"{{ appId:'{settings.AppId}',version:'{settings.Version}'"; - options = string.IsNullOrWhiteSpace(settings.FBInitParams) - ? string.Concat(options, "}") - : string.Concat(options, ",", settings.FBInitParams, "}"); - - script = $"window.fbAsyncInit = function(){{ FB.init({options});}};"; - } - } - - if (script != null) - { - var bytes = Encoding.UTF8.GetBytes(script); - await httpContext.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(script).AsMemory(0, bytes.Length), httpContext.RequestAborted); - - return; - } - } - - await _next.Invoke(httpContext); - } -} diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Startup.cs index 2b30ef8dd60..be95c6eee3a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Startup.cs @@ -21,7 +21,8 @@ public sealed class Startup : StartupBase { public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) { - builder.UseMiddleware(); + routes.AddInitSdkEndpoint(); + routes.AddLoadSdkEndpoint(); } public override void ConfigureServices(IServiceCollection services) diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Endpoints/Api/GetIntellisenseEndpoint.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Endpoints/Api/GetIntellisenseEndpoint.cs new file mode 100644 index 00000000000..308a52e3e74 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Endpoints/Api/GetIntellisenseEndpoint.cs @@ -0,0 +1,74 @@ +using Fluid; +using System.Text; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.Net.Http.Headers; +using OrchardCore.ContentManagement; +using OrchardCore.DisplayManagement.Liquid; +using OrchardCore.Environment.Shell.Configuration; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Caching.Memory; + +namespace OrchardCore.Liquid.Endpoints.Api; + +public static class GetIntellisenseEndpoint +{ + + public static IEndpointRouteBuilder AddGetIntellisenseEndpoint(this IEndpointRouteBuilder builder) + { + builder.MapGet("/OrchardCore.Liquid/Scripts/liquid-intellisense.js", HandleAsync) + .AllowAnonymous() + .DisableAntiforgery(); + + return builder; + } + + [Authorize(AuthenticationSchemes = "Api")] + private static Task HandleAsync(HttpContext httpContext, IMemoryCache cache) + { + const string cacheKey = "LiquidIntellisenseScript"; + + var cacheControl = $"public, max-age={TimeSpan.FromDays(30).TotalSeconds}, s-max-age={TimeSpan.FromDays(365.25).TotalSeconds}"; + if (cache.TryGetValue(cacheKey, out (byte[] Bytes, string ETag) cachedData)) + { + if (httpContext.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var etagHeader) && + etagHeader.Contains(cachedData.ETag)) + { + return Task.FromResult(Results.StatusCode(StatusCodes.Status304NotModified)); + } + + httpContext.Response.Headers[HeaderNames.CacheControl] = cacheControl; + httpContext.Response.Headers[HeaderNames.ContentType] = "application/javascript"; + httpContext.Response.Headers[HeaderNames.ETag] = cachedData.ETag; + + return Task.FromResult(Results.Bytes(cachedData.Bytes, "application/javascript")); + } + + var shellConfiguration = httpContext.RequestServices.GetRequiredService(); + cacheControl = shellConfiguration.GetValue("StaticFileOptions:CacheControl", cacheControl); + + var templateOptions = httpContext.RequestServices.GetRequiredService>(); + var liquidViewParser = httpContext.RequestServices.GetRequiredService(); + + var filters = string.Join(',', templateOptions.Value.Filters.Select(x => $"'{x.Key}'")); + var tags = string.Join(',', liquidViewParser.RegisteredTags.Select(x => $"'{x.Key}'")); + + var script = $@"[{filters}].forEach(value=>{{if(!liquidFilters.includes(value)){{ liquidFilters.push(value);}}}}); + [{tags}].forEach(value=>{{if(!liquidTags.includes(value)){{ liquidTags.push(value);}}}});"; + + var etag = Guid.NewGuid().ToString("n"); + var bytes = Encoding.UTF8.GetBytes(script); + cache.Set(cacheKey,(bytes,etag)); + + httpContext.Response.Headers[HeaderNames.CacheControl] = cacheControl; + httpContext.Response.Headers[HeaderNames.ContentType] = "application/javascript"; + httpContext.Response.Headers[HeaderNames.ETag] = etag; + + return Task.FromResult(Results.Bytes(bytes, "application/javascript")); + } + +} diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/ScriptsMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/ScriptsMiddleware.cs deleted file mode 100644 index 6c5c148f961..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/ScriptsMiddleware.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Text; -using Fluid; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Microsoft.Net.Http.Headers; -using OrchardCore.DisplayManagement.Liquid; -using OrchardCore.Environment.Shell.Configuration; - -namespace OrchardCore.Liquid; - -public class ScriptsMiddleware -{ - private readonly RequestDelegate _next; - private byte[] _bytes; - private string _etag; - - public ScriptsMiddleware(RequestDelegate next) - { - _next = next; - } - - public async Task Invoke(HttpContext httpContext) - { - if (httpContext.Request.Path.StartsWithSegments("/OrchardCore.Liquid/Scripts", StringComparison.OrdinalIgnoreCase)) - { - if (Path.GetFileName(httpContext.Request.Path.Value) == "liquid-intellisense.js") - { - if (httpContext.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var v)) - { - if (v.Contains(_etag)) - { - httpContext.Response.StatusCode = StatusCodes.Status304NotModified; - - return; - } - } - - var cacheControl = $"public, max-age={TimeSpan.FromDays(30).TotalSeconds}, s-max-age={TimeSpan.FromDays(365.25).TotalSeconds}"; - if (_bytes == null) - { - var templateOptions = httpContext.RequestServices.GetRequiredService>(); - var liquidViewParser = httpContext.RequestServices.GetRequiredService(); - var shellConfiguration = httpContext.RequestServices.GetRequiredService(); - cacheControl = shellConfiguration.GetValue("StaticFileOptions:CacheControl", cacheControl); - - var filters = string.Join(',', templateOptions.Value.Filters.Select(x => $"'{x.Key}'")); - var tags = string.Join(',', liquidViewParser.RegisteredTags.Select(x => $"'{x.Key}'")); - - var script = $@"[{filters}].forEach(value=>{{if(!liquidFilters.includes(value)){{ liquidFilters.push(value);}}}}); - [{tags}].forEach(value=>{{if(!liquidTags.includes(value)){{ liquidTags.push(value);}}}});"; - - _etag = Guid.NewGuid().ToString("n"); - _bytes = Encoding.UTF8.GetBytes(script); - } - - httpContext.Response.Headers[HeaderNames.CacheControl] = cacheControl; - httpContext.Response.Headers[HeaderNames.ContentType] = "application/javascript"; - httpContext.Response.Headers[HeaderNames.ETag] = _etag; - - await httpContext.Response.Body.WriteAsync(_bytes, httpContext.RequestAborted); - - return; - } - } - - await _next.Invoke(httpContext); - } -} diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Startup.cs index 2491270ca6a..5602c631653 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Startup.cs @@ -21,6 +21,7 @@ using OrchardCore.Liquid.ViewModels; using OrchardCore.Modules; using OrchardCore.ResourceManagement; +using OrchardCore.Liquid.Endpoints.Api; namespace OrchardCore.Liquid; @@ -28,11 +29,12 @@ public sealed class Startup : StartupBase { public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) { - app.UseMiddleware(); + routes.AddGetIntellisenseEndpoint(); } public override void ConfigureServices(IServiceCollection services) { + services.AddMemoryCache(); services.AddScoped(); services.Configure(options =>