diff --git a/EventHub.sln b/EventHub.sln index 9851ae4..3b8c6a4 100644 --- a/EventHub.sln +++ b/EventHub.sln @@ -65,6 +65,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.Web.Theme", "src\E EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.Admin.HttpApi.Host", "src\EventHub.Admin.HttpApi.Host\EventHub.Admin.HttpApi.Host.csproj", "{1434105B-DE2C-4AA2-A865-EBA0DBCCA5B0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.Web.Shared", "src\EventHub.Web.Shared\EventHub.Web.Shared.csproj", "{6F7DDDD3-1AC3-4304-AB2C-3BAB294F94AA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -163,6 +165,10 @@ Global {1434105B-DE2C-4AA2-A865-EBA0DBCCA5B0}.Debug|Any CPU.Build.0 = Debug|Any CPU {1434105B-DE2C-4AA2-A865-EBA0DBCCA5B0}.Release|Any CPU.ActiveCfg = Release|Any CPU {1434105B-DE2C-4AA2-A865-EBA0DBCCA5B0}.Release|Any CPU.Build.0 = Release|Any CPU + {6F7DDDD3-1AC3-4304-AB2C-3BAB294F94AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F7DDDD3-1AC3-4304-AB2C-3BAB294F94AA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F7DDDD3-1AC3-4304-AB2C-3BAB294F94AA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F7DDDD3-1AC3-4304-AB2C-3BAB294F94AA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -197,6 +203,7 @@ Global {69BC428E-5C49-4391-9985-0D15523D6922} = {F8CF0FE2-9F75-4940-92EC-3D021416DEBF} {E6D5BF0E-DE92-4D82-A352-EF04B37CB11C} = {F8CF0FE2-9F75-4940-92EC-3D021416DEBF} {1434105B-DE2C-4AA2-A865-EBA0DBCCA5B0} = {7016C0D2-D0D5-498B-9FA7-8ABA8F5C39EF} + {6F7DDDD3-1AC3-4304-AB2C-3BAB294F94AA} = {81F0EB50-4CF9-402D-B723-A07E95F8D7CB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6AAFA1C6-603E-13FA-45E5-7910AA9F661D} diff --git a/src/EventHub.Admin.HttpApi.Host/EventHub.Admin.HttpApi.Host.csproj b/src/EventHub.Admin.HttpApi.Host/EventHub.Admin.HttpApi.Host.csproj index f684632..0bde80d 100644 --- a/src/EventHub.Admin.HttpApi.Host/EventHub.Admin.HttpApi.Host.csproj +++ b/src/EventHub.Admin.HttpApi.Host/EventHub.Admin.HttpApi.Host.csproj @@ -26,6 +26,7 @@ + diff --git a/src/EventHub.Admin.HttpApi.Host/EventHubAdminHttpApiHostModule.cs b/src/EventHub.Admin.HttpApi.Host/EventHubAdminHttpApiHostModule.cs index 07c290d..5d4f910 100644 --- a/src/EventHub.Admin.HttpApi.Host/EventHubAdminHttpApiHostModule.cs +++ b/src/EventHub.Admin.HttpApi.Host/EventHubAdminHttpApiHostModule.cs @@ -7,6 +7,7 @@ using EventHub.Admin.Organizations; using EventHub.Admin.Utils; using EventHub.EntityFrameworkCore; using EventHub.Options; +using EventHub.Web.Shared; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Cors; @@ -42,7 +43,8 @@ namespace EventHub.Admin typeof(AbpCachingStackExchangeRedisModule), typeof(AbpAspNetCoreSerilogModule), typeof(AbpSwashbuckleModule), - typeof(AbpAspNetCoreMvcUiBasicThemeModule) + typeof(AbpAspNetCoreMvcUiBasicThemeModule), + typeof(EventHubWebSharedModule) )] public class EventHubAdminHttpApiHostModule : AbpModule { @@ -65,7 +67,7 @@ namespace EventHub.Admin ConfigureTiming(); ConfigureAutoApiControllers(); } - + private void ConfigureAutoApiControllers() { Configure(options => @@ -87,7 +89,7 @@ namespace EventHub.Admin { Configure(options => { options.KeyPrefix = "EventHub:"; }); } - + private void ConfigureTiming() { Configure(options => { options.Kind = DateTimeKind.Utc; }); @@ -185,7 +187,7 @@ namespace EventHub.Admin { context.Services.AddSameSiteCookiePolicy(); } - + public override void OnApplicationInitialization(ApplicationInitializationContext context) { var app = context.GetApplicationBuilder(); @@ -200,7 +202,7 @@ namespace EventHub.Admin { new CultureInfo("en") }; - + app.UseAbpRequestLocalization(options => { options.DefaultRequestCulture = new RequestCulture("en"); diff --git a/src/EventHub.HttpApi.Host/EventHub.HttpApi.Host.csproj b/src/EventHub.HttpApi.Host/EventHub.HttpApi.Host.csproj index 52e2743..7ddeb0a 100644 --- a/src/EventHub.HttpApi.Host/EventHub.HttpApi.Host.csproj +++ b/src/EventHub.HttpApi.Host/EventHub.HttpApi.Host.csproj @@ -26,6 +26,7 @@ + diff --git a/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs b/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs index 9e69fa8..e2b3ced 100644 --- a/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs +++ b/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs @@ -9,6 +9,7 @@ using EventHub.Options; using EventHub.Organizations; using EventHub.Organizations.Plans; using EventHub.Utils; +using EventHub.Web.Shared; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Cors; @@ -45,7 +46,8 @@ namespace EventHub typeof(AbpCachingStackExchangeRedisModule), typeof(AbpAspNetCoreSerilogModule), typeof(AbpSwashbuckleModule), - typeof(AbpAspNetCoreMvcUiBasicThemeModule) + typeof(AbpAspNetCoreMvcUiBasicThemeModule), + typeof(EventHubWebSharedModule) )] public class EventHubHttpApiHostModule : AbpModule { @@ -68,7 +70,7 @@ namespace EventHub ConfigureTiming(); ConfigurePremiumPlanInfo(context, configuration); } - + private void ConfigureAutoApiControllers() { Configure(options => @@ -96,7 +98,7 @@ namespace EventHub private void ConfigureVirtualFileSystem(ServiceConfigurationContext context) { var hostingEnvironment = context.Services.GetHostingEnvironment(); - + Configure(options => { options.FileSets.AddEmbedded( @@ -189,12 +191,12 @@ namespace EventHub { context.Services.AddSameSiteCookiePolicy(); } - + private void ConfigureTiming() { Configure(options => { options.Kind = DateTimeKind.Utc; }); } - + private void ConfigurePremiumPlanInfo(ServiceConfigurationContext context, IConfiguration configuration) { context.Services.AddOptions>() @@ -218,7 +220,7 @@ namespace EventHub { app.UseDeveloperExceptionPage(); } - + app.Use((context, next) => { context.Request.Scheme = "https"; @@ -229,7 +231,7 @@ namespace EventHub { new CultureInfo("en") }; - + app.UseAbpRequestLocalization(options => { options.DefaultRequestCulture = new RequestCulture("en"); diff --git a/src/EventHub.IdentityServer/EventHub.IdentityServer.csproj b/src/EventHub.IdentityServer/EventHub.IdentityServer.csproj index 0a0fdd6..43f48af 100644 --- a/src/EventHub.IdentityServer/EventHub.IdentityServer.csproj +++ b/src/EventHub.IdentityServer/EventHub.IdentityServer.csproj @@ -54,6 +54,7 @@ + diff --git a/src/EventHub.IdentityServer/EventHubIdentityServerModule.cs b/src/EventHub.IdentityServer/EventHubIdentityServerModule.cs index 9e345db..0bec194 100644 --- a/src/EventHub.IdentityServer/EventHubIdentityServerModule.cs +++ b/src/EventHub.IdentityServer/EventHubIdentityServerModule.cs @@ -5,6 +5,7 @@ using EventHub.Localization; using EventHub.Options; using EventHub.Utils; using EventHub.Web; +using EventHub.Web.Shared; using EventHub.Web.Theme; using EventHub.Web.Theme.Bundling; using IdentityServer4.Configuration; @@ -42,10 +43,11 @@ namespace EventHub typeof(AbpCachingStackExchangeRedisModule), typeof(AbpAccountWebIdentityServerModule), typeof(AbpAccountApplicationModule), - typeof(AbpAccountHttpApiModule), + typeof(AbpAccountHttpApiModule), typeof(EventHubWebThemeModule), typeof(EventHubEntityFrameworkCoreModule), - typeof(AbpAspNetCoreSerilogModule) + typeof(AbpAspNetCoreSerilogModule), + typeof(EventHubWebSharedModule) )] public class EventHubIdentityServerModule : AbpModule { @@ -70,7 +72,7 @@ namespace EventHub }); } } - + private X509Certificate2 GetSigningCertificate(IWebHostEnvironment hostingEnv, IConfiguration configuration) { var fileName = "account.openeventhub.pfx"; @@ -84,7 +86,7 @@ namespace EventHub return new X509Certificate2(file, passPhrase); } - + public override void ConfigureServices(ServiceConfigurationContext context) { var hostingEnvironment = context.Services.GetHostingEnvironment(); @@ -180,8 +182,8 @@ namespace EventHub .AllowCredentials(); }); }); - - context.Services.AddSameSiteCookiePolicy(); + + context.Services.AddSameSiteCookiePolicy(); } public override void OnApplicationInitialization(ApplicationInitializationContext context) @@ -196,10 +198,10 @@ namespace EventHub { ctx.SetIdentityServerOrigin(EventHubUrlOptions.GetAccountConfigValue(configuration)); } - + await next(); }); - + if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); @@ -211,7 +213,7 @@ namespace EventHub { app.UseErrorPage(); } - + app.UseCookiePolicy(); app.UseCorrelationId(); app.UseStaticFiles(); diff --git a/src/EventHub.Web.Shared/EventHub.Web.Shared.csproj b/src/EventHub.Web.Shared/EventHub.Web.Shared.csproj new file mode 100644 index 0000000..e895206 --- /dev/null +++ b/src/EventHub.Web.Shared/EventHub.Web.Shared.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/src/EventHub.Web.Shared/EventHubWebSharedModule.cs b/src/EventHub.Web.Shared/EventHubWebSharedModule.cs new file mode 100644 index 0000000..4f476ad --- /dev/null +++ b/src/EventHub.Web.Shared/EventHubWebSharedModule.cs @@ -0,0 +1,15 @@ +using EventHub.EntityFrameworkCore; +using EventHub.Web.Shared.HealthChecks; +using Volo.Abp.AspNetCore; +using Volo.Abp.Modularity; + +namespace EventHub.Web.Shared; + +[DependsOn(typeof(AbpAspNetCoreModule), typeof(EventHubEntityFrameworkCoreModule))] +public class EventHubWebSharedModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddEventHubHealthChecks(); + } +} diff --git a/src/EventHub.Web.Shared/HealthChecks/EventHubDatabaseCheck.cs b/src/EventHub.Web.Shared/HealthChecks/EventHubDatabaseCheck.cs new file mode 100644 index 0000000..2e008f7 --- /dev/null +++ b/src/EventHub.Web.Shared/HealthChecks/EventHubDatabaseCheck.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Identity; +using IdentityRole = Microsoft.AspNetCore.Identity.IdentityRole; + +namespace EventHub.Web.Shared.HealthChecks; + +public class EventHubDatabaseCheck : IHealthCheck, ITransientDependency +{ + protected readonly IIdentityRoleRepository IdentityRoleRepository; + + public EventHubDatabaseCheck(IIdentityRoleRepository identityRoleRepository) + { + IdentityRoleRepository = identityRoleRepository; + } + + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + await IdentityRoleRepository.GetListAsync(sorting: nameof(IdentityRole.Id), maxResultCount: 1, cancellationToken: cancellationToken); + + return HealthCheckResult.Healthy($"Could connect to database and get record."); + } + catch (Exception e) + { + return HealthCheckResult.Unhealthy($"Error when trying to get database record. ", e); + } + } +} diff --git a/src/EventHub.Web.Shared/HealthChecks/HealthChecksBuilderExtensions.cs b/src/EventHub.Web.Shared/HealthChecks/HealthChecksBuilderExtensions.cs new file mode 100644 index 0000000..6f2546a --- /dev/null +++ b/src/EventHub.Web.Shared/HealthChecks/HealthChecksBuilderExtensions.cs @@ -0,0 +1,76 @@ +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; + +namespace EventHub.Web.Shared.HealthChecks; + +public static class HealthChecksBuilderExtensions +{ + public static void AddEventHubHealthChecks(this IServiceCollection services) + { + // Add your health checks here + var healthChecksBuilder = services.AddHealthChecks(); + healthChecksBuilder.AddCheck("EventHub DbContext Check", tags: new string[] { "database" }); + + services.ConfigureHealthCheckEndpoint("/health-status"); + + // If you don't want to add HealthChecksUI, remove following configurations. + var configuration = services.GetConfiguration(); + var healthCheckUrl = configuration["App:HealthCheckUrl"]; + + if (string.IsNullOrEmpty(healthCheckUrl)) + { + healthCheckUrl = "/health-status"; + } + + var healthChecksUiBuilder = services.AddHealthChecksUI(settings => + { + settings.AddHealthCheckEndpoint("EventHub Health Status", healthCheckUrl); + }); + + // Set your HealthCheck UI Storage here + healthChecksUiBuilder.AddInMemoryStorage(); + + services.MapHealthChecksUiEndpoints(options => + { + options.UIPath = "/health-ui"; + options.ApiPath = "/health-api"; + }); + } + + private static IServiceCollection ConfigureHealthCheckEndpoint(this IServiceCollection services, string path) + { + services.Configure(options => + { + options.EndpointConfigureActions.Add(endpointContext => + { + endpointContext.Endpoints.MapHealthChecks( + new PathString(path.EnsureStartsWith('/')), + new HealthCheckOptions + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse, + AllowCachingResponses = false, + }); + }); + }); + + return services; + } + + private static IServiceCollection MapHealthChecksUiEndpoints(this IServiceCollection services, Action? setupOption = null) + { + services.Configure(routerOptions => + { + routerOptions.EndpointConfigureActions.Add(endpointContext => + { + endpointContext.Endpoints.MapHealthChecksUI(setupOption); + }); + }); + + return services; + } +}