| | | 1 | | using Chronicis.Shared.DTOs; |
| | | 2 | | |
| | | 3 | | namespace Chronicis.Api.Services; |
| | | 4 | | |
| | | 5 | | public interface ISystemHealthService |
| | | 6 | | { |
| | | 7 | | Task<SystemHealthStatusDto> GetSystemHealthAsync(); |
| | | 8 | | } |
| | | 9 | | |
| | | 10 | | public sealed class SystemHealthService : ISystemHealthService |
| | | 11 | | { |
| | | 12 | | private readonly DatabaseHealthCheckService _databaseHealth; |
| | | 13 | | private readonly AzureOpenAIHealthCheckService _azureOpenAIHealth; |
| | | 14 | | private readonly BlobStorageHealthCheckService _blobStorageHealth; |
| | | 15 | | private readonly Auth0HealthCheckService _auth0Health; |
| | | 16 | | private readonly ILogger<SystemHealthService> _logger; |
| | | 17 | | |
| | | 18 | | public SystemHealthService( |
| | | 19 | | DatabaseHealthCheckService databaseHealth, |
| | | 20 | | AzureOpenAIHealthCheckService azureOpenAIHealth, |
| | | 21 | | BlobStorageHealthCheckService blobStorageHealth, |
| | | 22 | | Auth0HealthCheckService auth0Health, |
| | | 23 | | ILogger<SystemHealthService> logger) |
| | | 24 | | { |
| | 1 | 25 | | _databaseHealth = databaseHealth; |
| | 1 | 26 | | _azureOpenAIHealth = azureOpenAIHealth; |
| | 1 | 27 | | _blobStorageHealth = blobStorageHealth; |
| | 1 | 28 | | _auth0Health = auth0Health; |
| | 1 | 29 | | _logger = logger; |
| | 1 | 30 | | } |
| | | 31 | | |
| | | 32 | | public async Task<SystemHealthStatusDto> GetSystemHealthAsync() |
| | | 33 | | { |
| | | 34 | | var timestamp = DateTime.UtcNow; |
| | | 35 | | _logger.LogTraceSanitized("Starting system health check"); |
| | | 36 | | |
| | | 37 | | // Run all health checks in parallel |
| | | 38 | | var healthCheckTasks = new[] |
| | | 39 | | { |
| | | 40 | | _databaseHealth.CheckHealthAsync("Database", ServiceKeys.Database), |
| | | 41 | | _azureOpenAIHealth.CheckHealthAsync("Azure OpenAI", ServiceKeys.AzureOpenAI), |
| | | 42 | | _blobStorageHealth.CheckHealthAsync("Document Storage", ServiceKeys.BlobStorage), |
| | | 43 | | _auth0Health.CheckHealthAsync("Auth0", ServiceKeys.Auth0) |
| | | 44 | | }; |
| | | 45 | | |
| | | 46 | | // Add API self-check |
| | | 47 | | var apiHealthTask = Task.FromResult(new ServiceHealthDto |
| | | 48 | | { |
| | | 49 | | Name = "API", |
| | | 50 | | ServiceKey = ServiceKeys.Api, |
| | | 51 | | Status = HealthStatus.Healthy, |
| | | 52 | | Message = "API is responding", |
| | | 53 | | ResponseTimeMs = 0, |
| | | 54 | | CheckedAt = timestamp |
| | | 55 | | }); |
| | | 56 | | |
| | | 57 | | var allTasks = healthCheckTasks.Concat(new[] { apiHealthTask }).ToArray(); |
| | | 58 | | var results = await Task.WhenAll(allTasks); |
| | | 59 | | |
| | | 60 | | // Determine overall status |
| | | 61 | | var overallStatus = DetermineOverallStatus(results); |
| | | 62 | | |
| | | 63 | | _logger.LogTraceSanitized("System health check completed. Overall status: {Status}", overallStatus); |
| | | 64 | | |
| | | 65 | | return new SystemHealthStatusDto |
| | | 66 | | { |
| | | 67 | | Timestamp = timestamp, |
| | | 68 | | OverallStatus = overallStatus, |
| | | 69 | | Services = results.ToList() |
| | | 70 | | }; |
| | | 71 | | } |
| | | 72 | | |
| | | 73 | | private static string DetermineOverallStatus(ServiceHealthDto[] services) |
| | | 74 | | { |
| | 3 | 75 | | var hasUnhealthy = services.Any(s => s.Status == HealthStatus.Unhealthy); |
| | 3 | 76 | | var hasDegraded = services.Any(s => s.Status == HealthStatus.Degraded); |
| | | 77 | | |
| | 3 | 78 | | if (hasUnhealthy) |
| | 1 | 79 | | return HealthStatus.Unhealthy; |
| | | 80 | | |
| | 2 | 81 | | if (hasDegraded) |
| | 1 | 82 | | return HealthStatus.Degraded; |
| | | 83 | | |
| | 1 | 84 | | return HealthStatus.Healthy; |
| | | 85 | | } |
| | | 86 | | } |