diff --git a/src/FrontOffice.Main/ConfigureServices.cs b/src/FrontOffice.Main/ConfigureServices.cs index 9f07e21..82981c5 100644 --- a/src/FrontOffice.Main/ConfigureServices.cs +++ b/src/FrontOffice.Main/ConfigureServices.cs @@ -33,45 +33,149 @@ public static class ConfigureServices public static IServiceCollection AddGrpcServices(this IServiceCollection services, IConfiguration configuration) { var baseUrl = configuration["GwUrl"]; - var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler())); - httpClient.Timeout = TimeSpan.FromMinutes(10); // TODO Check Timeout - var serviceProvider = services.BuildServiceProvider(); - var channel = CreateAuthenticatedChannel(baseUrl, httpClient, serviceProvider); - services.AddSingleton(sp => new PackageContract.PackageContractClient(channel)); - services.AddSingleton(sp => new UserContract.UserContractClient(channel)); - services.AddSingleton(sp => new UserAddressContract.UserAddressContractClient(channel)); - services.AddSingleton(sp => new UserOrderContract.UserOrderContractClient(channel)); + // Register HttpClient for gRPC + services.AddScoped(sp => + { + var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler())); + httpClient.Timeout = TimeSpan.FromMinutes(10); + return httpClient; + }); + + // Register gRPC clients as scoped services + services.AddScoped(sp => + { + var httpClient = sp.GetRequiredService(); + var localStorage = sp.GetRequiredService(); + + var credentials = CallCredentials.FromInterceptor(async (context, metadata) => + { + try + { + var token = await localStorage.GetItemAsync("auth:token"); + if (!string.IsNullOrWhiteSpace(token)) + { + metadata.Add("Authorization", $"Bearer {token}"); + } + } + catch (Exception) + { + // Ignore errors during token retrieval + } + }); + + var channel = GrpcChannel.ForAddress(baseUrl, new GrpcChannelOptions + { + UnsafeUseInsecureChannelCallCredentials = true, + Credentials = ChannelCredentials.Create(new SslCredentials(), credentials), + HttpClient = httpClient, + MaxReceiveMessageSize = 1000 * 1024 * 1024, // 1 GB + MaxSendMessageSize = 1000 * 1024 * 1024 // 1 GB + }); + + return new PackageContract.PackageContractClient(channel); + }); + + services.AddScoped(sp => + { + var httpClient = sp.GetRequiredService(); + var localStorage = sp.GetRequiredService(); + + var credentials = CallCredentials.FromInterceptor(async (context, metadata) => + { + try + { + var token = await localStorage.GetItemAsync("auth:token"); + if (!string.IsNullOrWhiteSpace(token)) + { + metadata.Add("Authorization", $"Bearer {token}"); + } + } + catch (Exception) + { + // Ignore errors during token retrieval + } + }); + + var channel = GrpcChannel.ForAddress(baseUrl, new GrpcChannelOptions + { + UnsafeUseInsecureChannelCallCredentials = true, + Credentials = ChannelCredentials.Create(new SslCredentials(), credentials), + HttpClient = httpClient, + MaxReceiveMessageSize = 1000 * 1024 * 1024, // 1 GB + MaxSendMessageSize = 1000 * 1024 * 1024 // 1 GB + }); + + return new UserContract.UserContractClient(channel); + }); + + services.AddScoped(sp => + { + var httpClient = sp.GetRequiredService(); + var localStorage = sp.GetRequiredService(); + + var credentials = CallCredentials.FromInterceptor(async (context, metadata) => + { + try + { + var token = await localStorage.GetItemAsync("auth:token"); + if (!string.IsNullOrWhiteSpace(token)) + { + metadata.Add("Authorization", $"Bearer {token}"); + } + } + catch (Exception) + { + // Ignore errors during token retrieval + } + }); + + var channel = GrpcChannel.ForAddress(baseUrl, new GrpcChannelOptions + { + UnsafeUseInsecureChannelCallCredentials = true, + Credentials = ChannelCredentials.Create(new SslCredentials(), credentials), + HttpClient = httpClient, + MaxReceiveMessageSize = 1000 * 1024 * 1024, // 1 GB + MaxSendMessageSize = 1000 * 1024 * 1024 // 1 GB + }); + + return new UserAddressContract.UserAddressContractClient(channel); + }); + + services.AddScoped(sp => + { + var httpClient = sp.GetRequiredService(); + var localStorage = sp.GetRequiredService(); + + var credentials = CallCredentials.FromInterceptor(async (context, metadata) => + { + try + { + var token = await localStorage.GetItemAsync("auth:token"); + if (!string.IsNullOrWhiteSpace(token)) + { + metadata.Add("Authorization", $"Bearer {token}"); + } + } + catch (Exception) + { + // Ignore errors during token retrieval + } + }); + + var channel = GrpcChannel.ForAddress(baseUrl, new GrpcChannelOptions + { + UnsafeUseInsecureChannelCallCredentials = true, + Credentials = ChannelCredentials.Create(new SslCredentials(), credentials), + HttpClient = httpClient, + MaxReceiveMessageSize = 1000 * 1024 * 1024, // 1 GB + MaxSendMessageSize = 1000 * 1024 * 1024 // 1 GB + }); + + return new UserOrderContract.UserOrderContractClient(channel); + }); return services; } - private static GrpcChannel CreateAuthenticatedChannel(string address, HttpClient httpClient, IServiceProvider serviceProvider) - { - var credentials = CallCredentials.FromInterceptor(async (context, metadata) => - { - // Get token from local storage if available - var localStorage = serviceProvider.GetService(typeof(ILocalStorageService)) as ILocalStorageService; - if (localStorage != null) - { - var token = await localStorage.GetItemAsync("auth:token"); - if (!string.IsNullOrWhiteSpace(token)) - { - metadata.Add("Authorization", $"Bearer {token}"); - } - } - }); - - // SslCredentials is used here because this channel is using TLS. - // CallCredentials can't be used with ChannelCredentials.Insecure on non-TLS channels. - var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions - { - UnsafeUseInsecureChannelCallCredentials = true, - Credentials = ChannelCredentials.Create(new SslCredentials(), credentials), - HttpClient = httpClient, - MaxReceiveMessageSize = 1000 * 1024 * 1024, // 1 GB - MaxSendMessageSize = 1000 * 1024 * 1024 // 1 GB - }); - return channel; - } }