diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj
index 50e372791f..fad2607adb 100644
--- a/src/Billing/Billing.csproj
+++ b/src/Billing/Billing.csproj
@@ -13,6 +13,7 @@
+
diff --git a/src/Billing/HealthChecks/StripeHealthCheck.cs b/src/Billing/HealthChecks/StripeHealthCheck.cs
new file mode 100644
index 0000000000..18c0456265
--- /dev/null
+++ b/src/Billing/HealthChecks/StripeHealthCheck.cs
@@ -0,0 +1,25 @@
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Stripe;
+
+namespace Bit.Billing.HealthChecks;
+
+public class StripeHealthCheck : IHealthCheck
+{
+ public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new())
+ {
+ try
+ {
+ var accountService = new AccountService();
+ _ = await accountService.ListAsync(
+ new AccountListOptions { Limit = 1 },
+ null,
+ cancellationToken);
+
+ return HealthCheckResult.Healthy();
+ }
+ catch (StripeException)
+ {
+ return HealthCheckResult.Unhealthy("Stripe is down.");
+ }
+ }
+}
diff --git a/src/Billing/Startup.cs b/src/Billing/Startup.cs
index e9f2f53488..b6a3c04e4c 100644
--- a/src/Billing/Startup.cs
+++ b/src/Billing/Startup.cs
@@ -1,5 +1,6 @@
using System.Globalization;
using System.Net.Http.Headers;
+using Bit.Billing.HealthChecks;
using Bit.Billing.Services;
using Bit.Billing.Services.Implementations;
using Bit.Core.Billing.Extensions;
@@ -8,7 +9,9 @@ using Bit.Core.SecretsManager.Repositories;
using Bit.Core.SecretsManager.Repositories.Noop;
using Bit.Core.Settings;
using Bit.Core.Utilities;
+using Bit.Infrastructure.EntityFramework.Repositories;
using Bit.SharedWeb.Utilities;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Quartz;
using Stripe;
@@ -88,10 +91,7 @@ public class Startup
services.AddScoped();
// Mvc
- services.AddMvc(config =>
- {
- config.Filters.Add(new LoggingExceptionHandlerFilterAttribute());
- });
+ services.AddMvc(config => { config.Filters.Add(new LoggingExceptionHandlerFilterAttribute()); });
services.Configure(options => options.LowercaseUrls = true);
// Authentication
@@ -99,20 +99,19 @@ public class Startup
// Set up HttpClients
services.AddHttpClient("FreshdeskApi");
- services.AddHttpClient("OnyxApi", client =>
- {
- client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", billingSettings.Onyx.ApiKey);
- });
+ services.AddHttpClient("OnyxApi",
+ client =>
+ {
+ client.DefaultRequestHeaders.Authorization =
+ new AuthenticationHeaderValue("Bearer", billingSettings.Onyx.ApiKey);
+ });
services.AddScoped();
services.AddScoped();
services.AddScoped();
// Add Quartz services first
- services.AddQuartz(q =>
- {
- q.UseMicrosoftDependencyInjectionJobFactory();
- });
+ services.AddQuartz(q => { q.UseMicrosoftDependencyInjectionJobFactory(); });
services.AddQuartzHostedService();
// Jobs service
@@ -122,6 +121,10 @@ public class Startup
// Swagger
services.AddEndpointsApiExplorer();
services.AddSwaggerGen();
+
+ services.AddHealthChecks()
+ .AddDbContextCheck("ef", tags: ["db"])
+ .AddCheck("stripe", tags: ["ext-api"]);
}
public void Configure(
@@ -149,6 +152,18 @@ public class Startup
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
- app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapDefaultControllerRoute();
+
+ endpoints.MapHealthChecks("/healthz", new HealthCheckOptions
+ {
+ Predicate = registration =>
+ {
+ string[] tags = ["db", "ext-api"];
+ return registration.Tags.Any(tag => tags.Contains(tag));
+ }
+ });
+ });
}
}