diff --git a/test/SharedKernel.Demo/Program.cs b/Shared.Kernel.Demo/Program.cs similarity index 94% rename from test/SharedKernel.Demo/Program.cs rename to Shared.Kernel.Demo/Program.cs index 34442b7..dcc1c9d 100644 --- a/test/SharedKernel.Demo/Program.cs +++ b/Shared.Kernel.Demo/Program.cs @@ -1,8 +1,7 @@ -using DistributedCache.Extensions; using DistributedCache.Options; using FluentMinimalApiMapper; using Microsoft.AspNetCore.Mvc; -using SharedKernel.Demo; +using SharedKernel.Demo2; using ResponseCrafter.Enums; using ResponseCrafter.Extensions; using SharedKernel.Extensions; @@ -18,7 +17,7 @@ AssemblyRegistry.Add(typeof(Program).Assembly); builder - .AddPandaVault() + // .AddPandaVault() .AddSerilog() .AddResponseCrafter(NamingConvention.ToSnakeCase) .AddOpenApi() @@ -48,6 +47,7 @@ .UseOpenApi() .MapControllers(); + app.MapPost("/params", ([AsParameters] TestTypes testTypes) => TypedResults.Ok(testTypes)); app.MapPost("/body", ([FromBody] TestTypes testTypes) => TypedResults.Ok(testTypes)); @@ -55,7 +55,7 @@ app.LogStartSuccess(); app.Run(); -namespace SharedKernel.Demo +namespace SharedKernel.Demo2 { public class TestTypes { diff --git a/test/SharedKernel.Demo/Properties/launchSettings.json b/Shared.Kernel.Demo/Properties/launchSettings.json similarity index 66% rename from test/SharedKernel.Demo/Properties/launchSettings.json rename to Shared.Kernel.Demo/Properties/launchSettings.json index ccb80cf..82484c7 100644 --- a/test/SharedKernel.Demo/Properties/launchSettings.json +++ b/Shared.Kernel.Demo/Properties/launchSettings.json @@ -1,11 +1,10 @@ { - "$schema": "http://json.schemastore.org/launchsettings.json", + "$schema": "https://json.schemastore.org/launchsettings.json", "profiles": { "http": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": true, - "launchUrl": "swagger", + "launchBrowser": false, "applicationUrl": "http://localhost:80", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/Shared.Kernel.Demo/Shared.Kernel.Demo.csproj b/Shared.Kernel.Demo/Shared.Kernel.Demo.csproj new file mode 100644 index 0000000..0da48ae --- /dev/null +++ b/Shared.Kernel.Demo/Shared.Kernel.Demo.csproj @@ -0,0 +1,26 @@ + + + + net9.0 + enable + enable + + + + + + + + + + + + + Never + + + Never + + + + diff --git a/test/SharedKernel.Demo/appsettings.Development.json b/Shared.Kernel.Demo/appsettings.Development.json similarity index 100% rename from test/SharedKernel.Demo/appsettings.Development.json rename to Shared.Kernel.Demo/appsettings.Development.json diff --git a/test/SharedKernel.Demo/appsettings.json b/Shared.Kernel.Demo/appsettings.json similarity index 100% rename from test/SharedKernel.Demo/appsettings.json rename to Shared.Kernel.Demo/appsettings.json diff --git a/SharedKernel.sln b/SharedKernel.sln index 38d00f3..0d3438f 100644 --- a/SharedKernel.sln +++ b/SharedKernel.sln @@ -4,8 +4,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedKernel", "src\SharedK EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedKernel.Tests", "test\SharedKernel.Tests\SharedKernel.Tests.csproj", "{0305E58F-1C47-454C-B10B-A223F2561A85}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedKernel.Demo", "test\SharedKernel.Demo\SharedKernel.Demo.csproj", "{8A6AA36D-1CEF-4018-9C9D-7D029F3EAECE}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F8A6DCFE-8924-49A4-B3E9-2034593F54E5}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{FEE159A2-74A0-4469-9B93-52987CA1A3CA}" @@ -19,6 +17,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared.Kernel.Demo", "Shared.Kernel.Demo\Shared.Kernel.Demo.csproj", "{1CD76A30-4A74-4F54-AC0C-AEDD92408553}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,14 +33,14 @@ Global {0305E58F-1C47-454C-B10B-A223F2561A85}.Debug|Any CPU.Build.0 = Debug|Any CPU {0305E58F-1C47-454C-B10B-A223F2561A85}.Release|Any CPU.ActiveCfg = Release|Any CPU {0305E58F-1C47-454C-B10B-A223F2561A85}.Release|Any CPU.Build.0 = Release|Any CPU - {8A6AA36D-1CEF-4018-9C9D-7D029F3EAECE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8A6AA36D-1CEF-4018-9C9D-7D029F3EAECE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8A6AA36D-1CEF-4018-9C9D-7D029F3EAECE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8A6AA36D-1CEF-4018-9C9D-7D029F3EAECE}.Release|Any CPU.Build.0 = Release|Any CPU + {1CD76A30-4A74-4F54-AC0C-AEDD92408553}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1CD76A30-4A74-4F54-AC0C-AEDD92408553}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1CD76A30-4A74-4F54-AC0C-AEDD92408553}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1CD76A30-4A74-4F54-AC0C-AEDD92408553}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {25001943-A870-4E17-A9B9-0D190CEC819B} = {F8A6DCFE-8924-49A4-B3E9-2034593F54E5} - {8A6AA36D-1CEF-4018-9C9D-7D029F3EAECE} = {FEE159A2-74A0-4469-9B93-52987CA1A3CA} {0305E58F-1C47-454C-B10B-A223F2561A85} = {FEE159A2-74A0-4469-9B93-52987CA1A3CA} + {1CD76A30-4A74-4F54-AC0C-AEDD92408553} = {FEE159A2-74A0-4469-9B93-52987CA1A3CA} EndGlobalSection EndGlobal diff --git a/src/SharedKernel/OpenApi/EmbeddedFilesExtension.cs b/src/SharedKernel/OpenApi/EmbeddedFilesExtension.cs new file mode 100644 index 0000000..a3aeded --- /dev/null +++ b/src/SharedKernel/OpenApi/EmbeddedFilesExtension.cs @@ -0,0 +1,65 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; + +namespace SharedKernel.OpenApi; + +internal static class EmbeddedFilesExtension +{ + private static readonly HashSet AllowedResources = new(StringComparer.OrdinalIgnoreCase) + { + "panda-style.css", + "panda-style.js", + "favicon.svg", + "logo.svg", + "logo-wording.svg" + }; + + internal static WebApplication MapSwaggerUiAssetEndpoint(this WebApplication app) + { + app.Map("/swagger-resources/{resourceName}", async (HttpContext context, string resourceName) => + { + if (!AllowedResources.Contains(resourceName)) + { + context.Response.StatusCode = StatusCodes.Status404NotFound; + await context.Response.WriteAsync($"Resource '{resourceName}' not found."); + return; + } + + var assembly = typeof(EmbeddedFilesExtension).Assembly; + var resourcePath = assembly.GetManifestResourceNames() + .FirstOrDefault(x => x.EndsWith(resourceName, StringComparison.OrdinalIgnoreCase)); + + if (resourcePath == null) + { + context.Response.StatusCode = StatusCodes.Status500InternalServerError; + await context.Response.WriteAsync($"Resource '{resourceName}' not found in assembly."); + return; + } + + await using var stream = assembly.GetManifestResourceStream(resourcePath); + if (stream == null) + { + context.Response.StatusCode = StatusCodes.Status500InternalServerError; + await context.Response.WriteAsync($"Failed to load resource '{resourceName}'."); + return; + } + + context.Response.ContentType = GetContentType(resourceName); + await stream.CopyToAsync(context.Response.Body); + }) + .WithGroupName("SwaggerUiAssetEndpoint"); + + return app; + } + + private static string GetContentType(string resourceName) => + Path.GetExtension(resourceName).ToLowerInvariant() switch + { + ".css" => "text/css", + ".js" => "application/javascript", + ".svg" => "image/svg+xml", + ".png" => "image/png", + ".jpg" or ".jpeg" => "image/jpeg", + _ => "application/octet-stream" + }; +} \ No newline at end of file diff --git a/src/SharedKernel/OpenApi/OpenApiExtensions.cs b/src/SharedKernel/OpenApi/OpenApiExtensions.cs index 9df248f..1d27c93 100644 --- a/src/SharedKernel/OpenApi/OpenApiExtensions.cs +++ b/src/SharedKernel/OpenApi/OpenApiExtensions.cs @@ -15,6 +15,7 @@ public static class OpenApiExtensions public static WebApplicationBuilder AddOpenApi(this WebApplicationBuilder builder, Action? configureOptions = null) { + var openApiConfiguration = builder.Configuration .GetSection("OpenApi") .Get(); @@ -51,14 +52,10 @@ public static WebApplication UseOpenApi(this WebApplication app) return app; } - app.MapStaticAssets(); app.MapOpenApi(); + app.MapSwaggerUiAssetEndpoint(); app.MapSwaggerUi(openApiConfiguration); - app.MapScalarApiReference(options => - { - options.Theme = ScalarTheme.Kepler; - options.Favicon = "/assets/images/favicon.svg"; - }); + app.MapScalarUi(); return app; } diff --git a/test/SharedKernel.Demo/wwwroot/assets/images/favicon.svg b/src/SharedKernel/OpenApi/UiAssets/favicon.svg similarity index 100% rename from test/SharedKernel.Demo/wwwroot/assets/images/favicon.svg rename to src/SharedKernel/OpenApi/UiAssets/favicon.svg diff --git a/test/SharedKernel.Demo/wwwroot/assets/images/logo-wording.svg b/src/SharedKernel/OpenApi/UiAssets/logo-wording.svg similarity index 100% rename from test/SharedKernel.Demo/wwwroot/assets/images/logo-wording.svg rename to src/SharedKernel/OpenApi/UiAssets/logo-wording.svg diff --git a/test/SharedKernel.Demo/wwwroot/assets/images/logo.svg b/src/SharedKernel/OpenApi/UiAssets/logo.svg similarity index 100% rename from test/SharedKernel.Demo/wwwroot/assets/images/logo.svg rename to src/SharedKernel/OpenApi/UiAssets/logo.svg diff --git a/test/SharedKernel.Demo/wwwroot/assets/css/panda-style.css b/src/SharedKernel/OpenApi/UiAssets/panda-style.css similarity index 95% rename from test/SharedKernel.Demo/wwwroot/assets/css/panda-style.css rename to src/SharedKernel/OpenApi/UiAssets/panda-style.css index 2501eed..bac3c9a 100644 --- a/test/SharedKernel.Demo/wwwroot/assets/css/panda-style.css +++ b/src/SharedKernel/OpenApi/UiAssets/panda-style.css @@ -58,7 +58,7 @@ element.style { .swagger-ui .topbar-wrapper .link::before { content: ""; - background: url("../images/logo.svg") no-repeat center; + background: url("/swagger-resources/logo.svg") no-repeat center; background-size: contain; width: 36.402px; height: 28.8px; @@ -69,7 +69,7 @@ element.style { /* Add your second image */ .swagger-ui .topbar-wrapper .link::after { content: ""; - background: url("../images/logo-wording.svg") no-repeat center; + background: url("/swagger-resources/logo-wording.svg") no-repeat center; background-size: contain; width: 90.0276px; height: 20.2044px; diff --git a/src/SharedKernel/OpenApi/UiAssets/panda-style.js b/src/SharedKernel/OpenApi/UiAssets/panda-style.js new file mode 100644 index 0000000..601cf3f --- /dev/null +++ b/src/SharedKernel/OpenApi/UiAssets/panda-style.js @@ -0,0 +1,29 @@ +document.addEventListener('DOMContentLoaded', function () { + + const faviconPath = "/swagger-resources/favicon.svg"; + + const existingLink = document.querySelector("link[rel*='icon']"); + if (existingLink) { + existingLink.href = faviconPath; + } else { + const newLink = document.createElement("link"); + newLink.type = "image/svg+xml"; + newLink.rel = "icon"; + newLink.href = faviconPath; + document.head.appendChild(newLink); + } + + // Scroll modal to top when it appears + const observer = new MutationObserver(() => { + const modal = document.querySelector('.modal-ux-content'); + if (modal) { + modal.scrollTop = 0; + observer.disconnect(); + } + }); + + observer.observe(document.body, { + childList: true, + subtree: true, + }); +}); diff --git a/src/SharedKernel/OpenApi/UiExtensions.cs b/src/SharedKernel/OpenApi/UiExtensions.cs index b1a7d54..3b6e78b 100644 --- a/src/SharedKernel/OpenApi/UiExtensions.cs +++ b/src/SharedKernel/OpenApi/UiExtensions.cs @@ -1,6 +1,8 @@ +using System.Reflection; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Scalar.AspNetCore; +using SharedKernel.Extensions; using SharedKernel.OpenApi.Options; using Swashbuckle.AspNetCore.SwaggerUI; @@ -50,7 +52,7 @@ internal static WebApplication MapScalarUi(this WebApplication app) options.Theme = ScalarTheme.Kepler; if (scalarConfig?.FaviconPath is not null) { - options.Favicon = "/assets/images/favicon.svg"; + options.Favicon = "/swagger-resources/favicon.svg"; } }); return app; @@ -68,16 +70,9 @@ private static SwaggerUIOptions AddPandaOptions(this SwaggerUIOptions options, S { return options; } - - foreach (var cssPath in swaggerUiConfig.InjectedCssPaths) - { - options.InjectStylesheet(cssPath); - } - - foreach (var jsPath in swaggerUiConfig.InjectedJsPaths) - { - options.InjectJavascript(jsPath); - } + + options.InjectStylesheet("/swagger-resources/panda-style.css"); + options.InjectJavascript("/swagger-resources/panda-style.js"); return options; } diff --git a/src/SharedKernel/SharedKernel.csproj b/src/SharedKernel/SharedKernel.csproj index 31389d1..3e8a312 100644 --- a/src/SharedKernel/SharedKernel.csproj +++ b/src/SharedKernel/SharedKernel.csproj @@ -8,7 +8,7 @@ Readme.md Pandatech MIT - 1.0.4 + 1.0.5 Pandatech.SharedKernel Pandatech Shared Kernel Library Pandatech, shared kernel, library, OpenAPI, Swagger, utilities, scalar @@ -22,6 +22,10 @@ + + + + @@ -47,7 +51,7 @@ - + diff --git a/test/SharedKernel.Demo/SharedKernel.Demo.csproj b/test/SharedKernel.Demo/SharedKernel.Demo.csproj deleted file mode 100644 index 9639a95..0000000 --- a/test/SharedKernel.Demo/SharedKernel.Demo.csproj +++ /dev/null @@ -1,46 +0,0 @@ - - - - net9.0 - enable - enable - true - - - - - - - - - - - - - PreserveNewest - true - contentFiles\any\any - - - PreserveNewest - true - contentFiles\any\any - - - PreserveNewest - true - contentFiles\any\any - - - PreserveNewest - true - contentFiles\any\any - - - PreserveNewest - true - contentFiles\any\any - - - - diff --git a/test/SharedKernel.Demo/wwwroot/assets/js/docs.js b/test/SharedKernel.Demo/wwwroot/assets/js/docs.js deleted file mode 100644 index 91b8cc1..0000000 --- a/test/SharedKernel.Demo/wwwroot/assets/js/docs.js +++ /dev/null @@ -1,24 +0,0 @@ -document.addEventListener('DOMContentLoaded', function () { - - var link = document.querySelector("link[rel*='icon']") || document.createElement("link"); - document.head.removeChild(link); - link = document.createElement("link"); - link.type = "image/x-icon"; - link.rel = "shortcut icon"; - link.href = "../assets/images/favicon.svg"; - document.getElementsByTagName("head")[0].appendChild(link); - - // Adjusted MutationObserver code - const observer = new MutationObserver((mutations) => { - const modal = document.querySelector('.modal-ux-content'); - if (modal) { - modal.scrollTo(0, 0); - observer.disconnect(); - } - }); - - observer.observe(document.body, { - childList: true, - subtree: true, - }); -}); \ No newline at end of file