diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppClientCoordinator.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppClientCoordinator.cs index f1dbfb7d14..bd58c3e5b0 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppClientCoordinator.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppClientCoordinator.cs @@ -177,6 +177,7 @@ private void AuthenticationStateChanged(Task task) //#if (signalR == true) private void SubscribeToSignalREventsMessages() { + hubConnection.Remove(SignalREvents.SHOW_MESSAGE); signalROnDisposables.Add(hubConnection.On?, bool>(SignalREvents.SHOW_MESSAGE, async (message, data) => { logger.LogInformation("SignalR Message {Message} received from server to show.", message); @@ -209,17 +210,20 @@ private void SubscribeToSignalREventsMessages() // You can also leverage IPubSubService to notify other components in the application. })); + hubConnection.Remove(SignalREvents.PUBLISH_MESSAGE); signalROnDisposables.Add(hubConnection.On(SignalREvents.PUBLISH_MESSAGE, async (message, payload) => { logger.LogInformation("SignalR Message {Message} received from server to publish.", message); PubSubService.Publish(message, payload); })); + hubConnection.Remove(SignalREvents.EXCEPTION_THROWN); signalROnDisposables.Add(hubConnection.On(SignalREvents.EXCEPTION_THROWN, async (appProblemDetails) => { ExceptionHandler.Handle(appProblemDetails, displayKind: ExceptionDisplayKind.NonInterrupting); })); + hubConnection.Remove(SignalRMethods.UPLOAD_DIAGNOSTIC_LOGGER_STORE); signalROnDisposables.Add(hubConnection.On(SignalRMethods.UPLOAD_DIAGNOSTIC_LOGGER_STORE, async () => { return DiagnosticLogger.Store.ToArray(); @@ -308,8 +312,6 @@ await storageService.GetItem("Culture") ?? // 2- User settings private List signalROnDisposables = []; protected override async ValueTask DisposeAsync(bool disposing) { - await base.DisposeAsync(disposing); - unsubscribe?.Invoke(); NavigationManager.LocationChanged -= NavigationManager_LocationChanged; @@ -320,6 +322,9 @@ protected override async ValueTask DisposeAsync(bool disposing) hubConnection.Reconnected -= HubConnectionConnected; hubConnection.Reconnecting -= HubConnectionStateChange; signalROnDisposables.ForEach(d => d.Dispose()); + signalROnDisposables = []; //#endif + + await base.DisposeAsync(disposing); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs index 49e1ceff78..35f855bba2 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs @@ -167,11 +167,6 @@ public static IServiceCollection AddClientCoreProjectServices(this IServiceColle .WithAutomaticReconnect(sp.GetRequiredService()) .WithUrl(new Uri(absoluteServerAddressProvider.GetAddress(), "app-hub"), options => { - var telemetryContext = sp.GetRequiredService(); - - options.Headers.Add("X-App-Version", telemetryContext.AppVersion!); - options.Headers.Add("X-App-Platform", AppPlatform.Type.ToString()); - options.SkipNegotiation = false; // Required for Azure SignalR. options.Transports = HttpTransportType.WebSockets; // Avoid enabling long polling or Server-Sent Events. Focus on resolving the issue with WebSockets instead. diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Boilerplate.Client.Maui.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Boilerplate.Client.Maui.csproj index 6fc8bc7a34..07c53533c4 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Boilerplate.Client.Maui.csproj +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Boilerplate.Client.Maui.csproj @@ -103,8 +103,6 @@ - - diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Boilerplate.Client.Web.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Boilerplate.Client.Web.csproj index 9ecd983ea7..ae3f0df413 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Boilerplate.Client.Web.csproj +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Boilerplate.Client.Web.csproj @@ -9,8 +9,8 @@ true service-worker-assets.js Default + true - false true true diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props index 8fd9823073..a9c118e390 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props @@ -75,8 +75,6 @@ - - diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Product/ProductConfiguration.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Product/ProductConfiguration.cs index be96595205..20c4177f5b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Product/ProductConfiguration.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Product/ProductConfiguration.cs @@ -19,7 +19,7 @@ public void Configure(EntityTypeBuilder builder) //#if (database == "PostgreSQL" || database == "SqlServer") if (AppDbContext.IsEmbeddingEnabled) { - builder.Property(p => p.Embedding).HasColumnType("vector(768)"); // Checkout appsettings.json's AI:EmbeddingOptions:Dimensions + builder.Property(p => p.Embedding).HasColumnType("vector(384)"); // Dimensions depends on the model used, here assuming 384 because of LocalTextEmbeddingGenerationService. } else { 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 fdd8988f7a..d4094bc749 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 @@ -412,10 +412,6 @@ void AddDbContext(DbContextOptionsBuilder options) Endpoint = appSettings.AI.OpenAI.EmbeddingEndpoint, Transport = new HttpClientPipelineTransport(sp.GetRequiredService().CreateClient("AI")) }).AsIEmbeddingGenerator()) - .ConfigureOptions(options => - { - configuration.GetRequiredSection("AI:EmbeddingOptions").Bind(options); - }) .UseLogging() .UseOpenTelemetry(); // .UseDistributedCache() @@ -428,10 +424,6 @@ void AddDbContext(DbContextOptionsBuilder options) { Transport = new Azure.Core.Pipeline.HttpClientTransport(sp.GetRequiredService().CreateClient("AI")) }).AsIEmbeddingGenerator(appSettings.AI.AzureOpenAI.EmbeddingModel)) - .ConfigureOptions(options => - { - configuration.GetRequiredSection("AI:EmbeddingOptions").Bind(options); - }) .UseLogging() .UseOpenTelemetry(); // .UseDistributedCache() @@ -442,10 +434,6 @@ void AddDbContext(DbContextOptionsBuilder options) new Uri(appSettings.AI.HuggingFace.EmbeddingEndpoint), apiKey: appSettings.AI.HuggingFace.EmbeddingApiKey, httpClient: sp.GetRequiredService().CreateClient("AI"), loggerFactory: sp.GetRequiredService())) - .ConfigureOptions(options => - { - configuration.GetRequiredSection("AI:EmbeddingOptions").Bind(options); - }) .UseLogging() .UseOpenTelemetry(); // .UseDistributedCache() @@ -454,10 +442,6 @@ void AddDbContext(DbContextOptionsBuilder options) { services.AddEmbeddingGenerator(sp => new LocalTextEmbeddingGenerationService() .AsEmbeddingGenerator()) - .ConfigureOptions(options => - { - configuration.GetRequiredSection("AI:EmbeddingOptions").Bind(options); - }) .UseLogging() .UseOpenTelemetry(); // .UseDistributedCache() diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/SignalR/AppHub.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/SignalR/AppHub.cs index 9f2a396954..dbfa6ab699 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/SignalR/AppHub.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/SignalR/AppHub.cs @@ -28,8 +28,6 @@ public partial class AppHub : Hub public override async Task OnConnectedAsync() { - CheckClientAppVersion(); - if (Context.GetHttpContext()?.ContainsExpiredAccessToken() is true) throw new HubException(nameof(AppStrings.UnauthorizedException)).WithData("ConnectionId", Context.ConnectionId); @@ -97,18 +95,4 @@ private async Task ChangeAuthenticationStateImplementation(ClaimsPrincipal? user await dbContext.UserSessions.Where(us => us.SignalRConnectionId == Context.ConnectionId).ExecuteUpdateAsync(us => us.SetProperty(x => x.SignalRConnectionId, (string?)null)); } } - - private void CheckClientAppVersion() - { - if (Context.GetHttpContext()?.Request?.Headers?.TryGetValue("X-App-Version", out var appVersionHeaderValue) is true && appVersionHeaderValue.Any()) - { - var appVersion = appVersionHeaderValue.Single()!; - var appPlatformType = Enum.Parse(Context.GetHttpContext()!.Request.Headers["X-App-Platform"].Single()!); - var minimumSupportedVersion = settings.SupportedAppVersions!.GetMinimumSupportedAppVersion(appPlatformType); - if (minimumSupportedVersion != null && Version.Parse(appVersion) < minimumSupportedVersion) - throw new HubException(nameof(AppStrings.ForceUpdateTitle)) - .WithData("ClientAppVersion", appVersion) - .WithData("ConnectionId", Context.ConnectionId); - } - } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.json index cf687faf92..d001c1f9c3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.json @@ -39,9 +39,6 @@ "ChatOptions": { "Temperature": 0 }, - "EmbeddingOptions": { - "Dimensions": 768 - }, "ChatOptions_Comment": "Configures Temperature, TopP, TopK and the rest of the properties of Microsoft.Extensions.AI.ChatOptions", "OpenAI": { "ChatModel": "gpt-4.1-mini", diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.AppHost/Program.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.AppHost/Program.cs index 8136dd0a9d..5993ba0419 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.AppHost/Program.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.AppHost/Program.cs @@ -96,11 +96,12 @@ //#endif //#endif -// Blazor WebAssembly Standalone project. -builder.AddProject("clientwebwasm", "../../Client/Boilerplate.Client.Web/Boilerplate.Client.Web.csproj"); - if (builder.ExecutionContext.IsRunMode) // The following project is only added for testing purposes. { + // Blazor WebAssembly Standalone project. + builder.AddProject("clientwebwasm", "../../Client/Boilerplate.Client.Web/Boilerplate.Client.Web.csproj") + .WithExplicitStart(); + var mailpit = builder.AddMailPit("smtp") // For testing purposes only, in production, you would use a real SMTP server. .WithDataVolume("mailpit"); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.AppHost/appsettings.Development.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.AppHost/appsettings.Development.json index 0f5ef1f113..c09bafbf73 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.AppHost/appsettings.Development.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.AppHost/appsettings.Development.json @@ -17,8 +17,8 @@ "mysqlserver__Comment": "The username is `root` by default", //#endif //#if (filesStorage == "S3") - "minio-rootPassword": "P@ssw0rd", - "minio__Comment": "The username is `minioadmin` by default", + "s3-rootPassword": "P@ssw0rd", + "s3__Comment": "The username is `minioadmin` by default", //#endif "Comment": "You might need to delete the docker volumes to apply changes to the passwords" } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Extensions/WebApplicationBuilderExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Extensions/WebApplicationBuilderExtensions.cs index 154c269bf7..5efdce1fa1 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Extensions/WebApplicationBuilderExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Extensions/WebApplicationBuilderExtensions.cs @@ -132,14 +132,21 @@ private static TBuilder ConfigureOpenTelemetry(this TBuilder builder) .AddProcessor() .AddAspNetCoreInstrumentation(options => { - // Filter out Blazor static file requests + // Filter out Blazor static files and health checks requests. + string[] toBeIgnoredSegments = ["/health", + "/alive", + "/_content", + "/_framework"]; + options.Filter = context => { - if (context.Request.Path.HasValue is false) - return true; - var path = context.Request.Path.Value; - return path.StartsWith("/_framework", StringComparison.OrdinalIgnoreCase) is false && - path.StartsWith("/_content", StringComparison.OrdinalIgnoreCase) is false; + foreach (var segment in toBeIgnoredSegments) + { + if (context.Request.Path.StartsWithSegments(segment, StringComparison.OrdinalIgnoreCase)) + return false; + } + + return true; }; }) .AddHttpClientInstrumentation() diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Services/AppOpenTelemetryProcessor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Services/AppOpenTelemetryProcessor.cs index 61e747e063..5674d7061c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Services/AppOpenTelemetryProcessor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Services/AppOpenTelemetryProcessor.cs @@ -1,6 +1,7 @@ using OpenTelemetry; namespace Boilerplate.Server.Shared.Services; + public class AppOpenTelemetryProcessor : BaseProcessor { public override void OnStart(Activity activity) @@ -9,5 +10,9 @@ public override void OnStart(Activity activity) { activity.IsAllDataRequested = false; // Prevents Blazor Server's SignalR from being exported } + else if (activity.OperationName is "Microsoft.AspNetCore.Components.HandleEvent") + { + activity.IsAllDataRequested = false; // Prevents Blazor's events from being exported. + } } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json index 41fd1e3a54..aee46d131d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json @@ -36,9 +36,6 @@ "ChatOptions": { "Temperature": 0 }, - "EmbeddingOptions": { - "Dimensions": 768 - }, "OpenAI": { "ChatModel": "gpt-4.1-mini", "ChatApiKey": null, diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/TestsInitializer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/TestsInitializer.cs index cdcd437934..5349bd50e3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/TestsInitializer.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/TestsInitializer.cs @@ -101,14 +101,7 @@ private static async Task InitializeDatabase(AppTestServer testServer) } //#endif //#endif - if ((await dbContext.Database.GetPendingMigrationsAsync()).Any()) - { - await dbContext.Database.MigrateAsync(); - } - else if ((await dbContext.Database.GetAppliedMigrationsAsync()).Any() is false) - { - throw new InvalidOperationException("No migrations have been added. Please ensure that migrations are added before running tests."); - } + await dbContext.Database.EnsureCreatedAsync(); // It's recommended to start using ef-core migrations. } }