From 45b0edd0652cc55e532c1fae34521407129e3ede Mon Sep 17 00:00:00 2001 From: Yaser Moradi Date: Tue, 17 Dec 2024 19:24:19 +0100 Subject: [PATCH] feat(templates): add Blazor WASM Standalone samples to cd pipelines and a bunch of unrelated stuff #9506 (#9507) --- .github/workflows/admin-sample.cd.yml | 50 ++++++++++++ .github/workflows/todo-sample.cd.yml | 47 +++++++++++ README.md | 4 +- .../Components/App.razor | 47 ++++++----- .../Extensions/HttpRequestExtensions.cs | 19 ----- .../wwwroot/service-worker.published.js | 3 +- .../Program.Services.cs | 11 ++- .../ServerApiSettings.cs | 7 ++ .../Components/App.razor | 77 +++++++++---------- .../Extensions/HttpRequestExtensions.cs | 19 ----- .../Extensions/HttpRequestExtensions.cs | 22 ------ .../Extensions/HttpRequestExtensions.cs | 22 ------ .../Components/App.razor | 29 ++++--- .../Extensions/HttpRequestExtensions.cs | 22 ------ 14 files changed, 188 insertions(+), 191 deletions(-) diff --git a/.github/workflows/admin-sample.cd.yml b/.github/workflows/admin-sample.cd.yml index 0e20c83b5a..20c90a0b1e 100644 --- a/.github/workflows/admin-sample.cd.yml +++ b/.github/workflows/admin-sample.cd.yml @@ -69,6 +69,56 @@ jobs: path: ${{env.DOTNET_ROOT}}/server include-hidden-files: true # Required for wwwroot/.well-known folder + deploy_blazor_wasm_standalone: + name: build blazor wasm standalone + runs-on: ubuntu-24.04 + + steps: + + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + global-json-file: src/global.json + + - name: Create project from Boilerplate + run: | + cd src/Templates/Boilerplate && dotnet build -c Release + dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0 + dotnet new install Bit.Boilerplate.0.0.0.nupkg + cd ../../../ && dotnet new bit-bp --name AdminPanel --database PostgreSQL --sample Admin --appInsights --sentry --serverUrl ${{ env.SERVER_ADDRESS }} --filesStorage AzureBlobStorage --notification --captcha reCaptcha --signalR --framework net9.0 + + - name: Update core appsettings.json + uses: devops-actions/variable-substitution@v1.2 + with: + files: 'AdminPanel/src/Shared/appsettings.json, AdminPanel/src/Client/AdminPanel.Client.Core/appsettings.json, AdminPanel/src/Client/AdminPanel.Client.Web/appsettings.json, AdminPanel/src/Client/AdminPanel.Client.Web/appsettings.Production.json' + env: + ServerAddress: ${{ env.SERVER_ADDRESS }} + Logging.Sentry.Dsn: ${{ secrets.ADMINPANEL_SENTRY_DSN }} + GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }} + AdsPushVapid.PublicKey: ${{ secrets.ADMINPANEL_PUBLIC_VAPIDKEY }} + ApplicationInsights.ConnectionString: ${{ secrets.APPLICATION_INSIGHTS_CONNECTION_STRING }} + + - uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install wasm + run: cd src && dotnet workload install wasm-tools + + - name: Generate CSS/JS files + run: dotnet build AdminPanel/src/Client/AdminPanel.Client.Core/AdminPanel.Client.Core.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" --no-restore -c Release + + - name: Publish + run: dotnet publish AdminPanel/src/Client/AdminPanel.Client.Web/AdminPanel.Client.Web.csproj -c Release -p:PwaEnabled=true -o ${{env.DOTNET_ROOT}}/client -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" + + - name: Upload to asw + run: | + npm install -g @azure/static-web-apps-cli + swa deploy --deployment-token ${{ secrets.ADMINPANEL_ASW_TOKEN }} --env production --app-location ${{env.DOTNET_ROOT}}/client/wwwroot + deploy_api_blazor: name: deploy api + blazor needs: build_api_blazor diff --git a/.github/workflows/todo-sample.cd.yml b/.github/workflows/todo-sample.cd.yml index 387e890cd5..b668a00726 100644 --- a/.github/workflows/todo-sample.cd.yml +++ b/.github/workflows/todo-sample.cd.yml @@ -121,6 +121,53 @@ jobs: CLOUDFLARE_ZONE: ${{ secrets.BITPLATFORM_DEV_CLOUDFLARE_ZONE }} CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }} + deploy_blazor_wasm_standalone: + name: build blazor wasm standalone + runs-on: ubuntu-24.04 + + steps: + + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + global-json-file: src/global.json + + - name: Create project from Boilerplate + run: | + cd src/Templates/Boilerplate && dotnet build -c Release + dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0 + dotnet new install Bit.Boilerplate.0.0.0.nupkg + cd ../../../ && dotnet new bit-bp --name TodoSample --database PostgreSQL --sample Todo --serverUrl ${{ env.SERVER_ADDRESS }} --filesStorage AzureBlobStorage --notification --captcha reCaptcha --framework net9.0 + + - name: Update core appsettings.json + uses: devops-actions/variable-substitution@v1.2 + with: + files: 'TodoSample/src/Shared/appsettings.json, TodoSample/src/Client/TodoSample.Client.Core/appsettings.json, TodoSample/src/Client/TodoSample.Client.Web/appsettings.json, TodoSample/src/Client/TodoSample.Client.Web/appsettings.Production.json' + env: + ServerAddress: ${{ env.SERVER_ADDRESS }} + GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }} + + - uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install wasm + run: cd src && dotnet workload install wasm-tools + + - name: Generate CSS/JS files + run: dotnet build TodoSample/src/Client/TodoSample.Client.Core/TodoSample.Client.Core.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" --no-restore -c Release + + - name: Publish + run: dotnet publish TodoSample/src/Client/TodoSample.Client.Web/TodoSample.Client.Web.csproj -c Release -p:PwaEnabled=true -o ${{env.DOTNET_ROOT}}/client -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -p:RunAOTCompilation=true -p:MultilingualEnabled=false + + - name: Upload to asw + run: | + npm install -g @azure/static-web-apps-cli + swa deploy --deployment-token ${{ secrets.TODO_ASW_TOKEN }} --env production --app-location ${{env.DOTNET_ROOT}}/client/wwwroot + build_blazor_hybrid_windows: name: build blazor hybrid (windows) runs-on: windows-2022 diff --git a/README.md b/README.md index d189ce9e8f..d0ffb560b2 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,9 @@ The following apps are our open-source projects powered by the bit platform show 1. [bitplatform.dev](https://bitplatform.dev): .NET 9 Pre-rendered SPA with Blazor WebAssembly 2. [blazorui.bitplatform.dev](https://blazorui.bitplatform.dev): .NET 9 Pre-rendered PWA with Blazor WebAssembly 3. [todo.bitplatform.dev](https://todo.bitplatform.dev): .NET 8 Pre-rendered PWA with Blazor WebAssembly -5. [adminpanel.bitplatform.dev](https://adminpanel.bitplatform.dev): .NET 9 PWA with Blazor WebAssembly Standalone (Hosted on Cloudflare Pages) +5. [adminpanel.bitplatform.dev](https://adminpanel.bitplatform.dev): .NET 9 PWA with Blazor WebAssembly +6. [adminpanel.bitplatform.cc](https://adminpanel.bitplatform.cc): .NET 9 PWA with Blazor WebAssembly Standalone (Free Azure static web app) +7. [todo.bitplatform.cc](https://todo.bitplatform.cc): AOT Compiled .NET 9 PWA with Blazor WebAssembly Standalone (Free Azure static web app) [Todo](https://todo.bitplatform.dev) & [Adminpanel](https://adminpanel.bitplatform.dev) web apps will launch their respective Android and iOS applications if you have already installed them, mirroring the behavior of apps like YouTube and Instagram. diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor index 8d494028ae..97d974449b 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor @@ -60,34 +60,31 @@ } - @if (HttpContext.Request.IsCrawlerClient() is false) + + @if (AppRenderMode.PwaEnabled) + { + + + + } + else { - - @if (AppRenderMode.PwaEnabled) - { - - - - } - else - { - - } - - - - + } + }); + } + + + + diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Extensions/HttpRequestExtensions.cs b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Extensions/HttpRequestExtensions.cs index 6b0aa99f04..345f142315 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Extensions/HttpRequestExtensions.cs +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Extensions/HttpRequestExtensions.cs @@ -18,23 +18,4 @@ public static Uri GetBaseUrl(this HttpRequest req) return uriBuilder.Uri; } - - public static bool IsCrawlerClient(this HttpRequest request) - { - if (request.Headers.TryGetValue(HeaderNames.UserAgent, out StringValues userAgentHeaderValue) is false) - return false; - - string? userAgent = userAgentHeaderValue.FirstOrDefault(); - - if (userAgent is null) - return false; - - if (userAgent.Contains("google", StringComparison.InvariantCultureIgnoreCase)) return true; - - if (userAgent.Contains("bing", StringComparison.InvariantCultureIgnoreCase)) return true; - - if (userAgent.Contains("lighthouse", StringComparison.InvariantCultureIgnoreCase)) return true; - - return false; - } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js index ce34c6bc0c..463978c2b4 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js @@ -27,8 +27,7 @@ self.assetsExclude = [ /bit\.blazorui\.fluent-light\.css$/, // If a PDF viewer is needed in the PWA, remove these two lines: /pdfjs-4\.7\.76\.js$/, - /pdfjs-4\.7\.76-worker\.js$/, - /Boilerplate\.Client\.Web\.styles\.css$/ + /pdfjs-4\.7\.76-worker\.js$/ ]; self.externalAssets = [ { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs index 1bfd6afd87..b777853cb8 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs @@ -124,10 +124,15 @@ public static void AddServerApiProjectServices(this WebApplicationBuilder builde configuration.Bind(settings); var webClientUrl = settings.WebClientUrl; + var allowedOrigins = settings.Cors?.AllowedOrigins?.ToList() ?? []; - policy.SetIsOriginAllowed(origin => - AllowedOriginsRegex().IsMatch(origin) || - (string.IsNullOrEmpty(webClientUrl) is false && string.Equals(origin, webClientUrl, StringComparison.InvariantCultureIgnoreCase))) + if (string.IsNullOrEmpty(webClientUrl) is false) + { + allowedOrigins.Add(webClientUrl); + } + + policy.SetIsOriginAllowed(origin => AllowedOriginsRegex().IsMatch(origin) + || allowedOrigins.Any(o => string.Equals(o, origin, StringComparison.InvariantCultureIgnoreCase))) .AllowAnyHeader() .AllowAnyMethod() .WithExposedHeaders(HeaderNames.RequestId); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs index c810c39852..f0f6989863 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs @@ -39,6 +39,8 @@ public partial class ServerApiSettings : SharedSettings public ForwardedHeadersOptions? ForwardedHeaders { get; set; } + public CorsOptions? Cors { get; set; } + public override IEnumerable Validate(ValidationContext validationContext) { var validationResults = base.Validate(validationContext).ToList(); @@ -157,3 +159,8 @@ public partial class SmsOptions string.IsNullOrEmpty(TwilioAccountSid) is false && string.IsNullOrEmpty(TwilioAutoToken) is false; } + +public class CorsOptions +{ + public string[] AllowedOrigins { get; set; } = []; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Components/App.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Components/App.razor index 0e360e84d5..6608eeb272 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Components/App.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Components/App.razor @@ -68,49 +68,46 @@ - @if (HttpContext.Request.IsCrawlerClient() is false) - { - @*#if (framework == "net9.0")*@ - - @*#else*@ - @*#if (IsInsideProjectTemplate == true)*@ - /* - @*#endif)*@ - - @*#if (IsInsideProjectTemplate == true)*@ - */ - @*#endif)*@ + @*#if (framework == "net9.0")*@ + + @*#else*@ + @*#if (IsInsideProjectTemplate == true)*@ + /* @*#endif)*@ - - @if (serverWebSettings.WebAppRender.PwaEnabled) - { - - - - } - else - { - + @*#if (IsInsideProjectTemplate == true)*@ + */ + @*#endif)*@ + @*#endif)*@ + + @if (serverWebSettings.WebAppRender.PwaEnabled) + { + + + + } + else + { + - } - - @*#if (offlineDb == true)*@ - - @*#endif*@ - - - + } + }); + } + + @*#if (offlineDb == true)*@ + + @*#endif*@ + + + diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Extensions/HttpRequestExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Extensions/HttpRequestExtensions.cs index dcd8b949b2..da3516f4d6 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Extensions/HttpRequestExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Extensions/HttpRequestExtensions.cs @@ -18,23 +18,4 @@ internal static Uri GetBaseUrl(this HttpRequest req) return uriBuilder.Uri; } - - public static bool IsCrawlerClient(this HttpRequest request) - { - if (request.Headers.TryGetValue(HeaderNames.UserAgent, out StringValues userAgentHeaderValue) is false) - return false; - - string? userAgent = userAgentHeaderValue.FirstOrDefault(); - - if (userAgent is null) - return false; - - if (userAgent.Contains("google", StringComparison.InvariantCultureIgnoreCase)) return true; - - if (userAgent.Contains("bing", StringComparison.InvariantCultureIgnoreCase)) return true; - - if (userAgent.Contains("lighthouse", StringComparison.InvariantCultureIgnoreCase)) return true; - - return false; - } } diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Server/Extensions/HttpRequestExtensions.cs b/src/Websites/Careers/src/Bit.Websites.Careers.Server/Extensions/HttpRequestExtensions.cs index 12760a7e32..fa1264050d 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Server/Extensions/HttpRequestExtensions.cs +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Server/Extensions/HttpRequestExtensions.cs @@ -17,26 +17,4 @@ public static string GetBaseUrl(this HttpRequest req) return uriBuilder.Uri.AbsoluteUri; } - - public static bool IsCrawlerClient(this HttpRequest request) - { - var agent = GetLoweredUserAgent(request); - - if (agent.Contains("google")) return true; - - if (agent.Contains("bing")) return true; - - if (agent.Contains("lighthouse")) return true; - - return false; - } - - private static string GetLoweredUserAgent(HttpRequest request) - { - var userAgent = request.Headers[HeaderNames.UserAgent].ToString(); - - if (string.IsNullOrEmpty(userAgent)) return string.Empty; - - return userAgent.ToLowerInvariant(); - } } diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Extensions/HttpRequestExtensions.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Extensions/HttpRequestExtensions.cs index 12760a7e32..fa1264050d 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Extensions/HttpRequestExtensions.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Extensions/HttpRequestExtensions.cs @@ -17,26 +17,4 @@ public static string GetBaseUrl(this HttpRequest req) return uriBuilder.Uri.AbsoluteUri; } - - public static bool IsCrawlerClient(this HttpRequest request) - { - var agent = GetLoweredUserAgent(request); - - if (agent.Contains("google")) return true; - - if (agent.Contains("bing")) return true; - - if (agent.Contains("lighthouse")) return true; - - return false; - } - - private static string GetLoweredUserAgent(HttpRequest request) - { - var userAgent = request.Headers[HeaderNames.UserAgent].ToString(); - - if (string.IsNullOrEmpty(userAgent)) return string.Empty; - - return userAgent.ToLowerInvariant(); - } } diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Server/Components/App.razor b/src/Websites/Sales/src/Bit.Websites.Sales.Server/Components/App.razor index 3b99fd9576..8e4350796e 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Server/Components/App.razor +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Server/Components/App.razor @@ -42,23 +42,20 @@ - @if (HttpContext.Request.IsCrawlerClient() is false) - { - - + - - - } + } + }); + + + diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Server/Extensions/HttpRequestExtensions.cs b/src/Websites/Sales/src/Bit.Websites.Sales.Server/Extensions/HttpRequestExtensions.cs index 12760a7e32..fa1264050d 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Server/Extensions/HttpRequestExtensions.cs +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Server/Extensions/HttpRequestExtensions.cs @@ -17,26 +17,4 @@ public static string GetBaseUrl(this HttpRequest req) return uriBuilder.Uri.AbsoluteUri; } - - public static bool IsCrawlerClient(this HttpRequest request) - { - var agent = GetLoweredUserAgent(request); - - if (agent.Contains("google")) return true; - - if (agent.Contains("bing")) return true; - - if (agent.Contains("lighthouse")) return true; - - return false; - } - - private static string GetLoweredUserAgent(HttpRequest request) - { - var userAgent = request.Headers[HeaderNames.UserAgent].ToString(); - - if (string.IsNullOrEmpty(userAgent)) return string.Empty; - - return userAgent.ToLowerInvariant(); - } }