mirror of https://github.com/Squidex/squidex.git
18 changed files with 85 additions and 246 deletions
@ -1,27 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
|
|
||||
namespace Squidex.Infrastructure.Diagnostics |
|
||||
{ |
|
||||
public sealed class HealthCheckResult |
|
||||
{ |
|
||||
public bool IsHealthy { get; } |
|
||||
|
|
||||
public string Description { get; } |
|
||||
|
|
||||
public Dictionary<string, object> Data { get; } |
|
||||
|
|
||||
public HealthCheckResult(bool isHealthy, string description = null, Dictionary<string, object> data = null) |
|
||||
{ |
|
||||
IsHealthy = isHealthy; |
|
||||
Data = data; |
|
||||
Description = description; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,16 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
namespace Squidex.Infrastructure.Diagnostics |
|
||||
{ |
|
||||
public static class HealthCheckScopes |
|
||||
{ |
|
||||
public const string Any = "*"; |
|
||||
public const string Node = "node"; |
|
||||
public const string Cluster = "cluster"; |
|
||||
} |
|
||||
} |
|
||||
@ -1,20 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
|
|
||||
namespace Squidex.Infrastructure.Diagnostics |
|
||||
{ |
|
||||
public interface IHealthCheck |
|
||||
{ |
|
||||
IEnumerable<string> Scopes { get; } |
|
||||
|
|
||||
Task<HealthCheckResult> CheckHealthAsync(CancellationToken cancellationToken = default(CancellationToken)); |
|
||||
} |
|
||||
} |
|
||||
@ -1,113 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
using Microsoft.AspNetCore.Http; |
|
||||
using Squidex.Infrastructure; |
|
||||
using Squidex.Infrastructure.Diagnostics; |
|
||||
using Squidex.Infrastructure.Json; |
|
||||
|
|
||||
namespace Squidex.Pipeline.Diagnostics |
|
||||
{ |
|
||||
public sealed class HealthCheckMiddleware |
|
||||
{ |
|
||||
private const string Suffix = "HealthCheck"; |
|
||||
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(2); |
|
||||
|
|
||||
private readonly Dictionary<string, IHealthCheck> healthChecks; |
|
||||
private readonly IJsonSerializer serializer; |
|
||||
private readonly RequestDelegate next; |
|
||||
private readonly List<string> scopes; |
|
||||
|
|
||||
public HealthCheckMiddleware(IEnumerable<IHealthCheck> healthChecks, IJsonSerializer serializer, RequestDelegate next, string scopes) |
|
||||
{ |
|
||||
Guard.NotNull(healthChecks, nameof(healthChecks)); |
|
||||
Guard.NotNull(serializer, nameof(serializer)); |
|
||||
|
|
||||
this.healthChecks = healthChecks.ToDictionary(GetName); |
|
||||
this.next = next; |
|
||||
this.serializer = serializer; |
|
||||
this.scopes = SplitScopes(scopes); |
|
||||
} |
|
||||
|
|
||||
public async Task Invoke(HttpContext context) |
|
||||
{ |
|
||||
if (CanServeRequest(context.Request)) |
|
||||
{ |
|
||||
using (var cts = new CancellationTokenSource(Timeout)) |
|
||||
{ |
|
||||
var matchingChecks = healthChecks.Where(x => CanUseCheck(x.Value)); |
|
||||
|
|
||||
var results = await Task.WhenAll(matchingChecks.Select(x => MakeHealthCheckAsync(x.Key, x.Value, cts.Token))); |
|
||||
|
|
||||
context.Response.StatusCode = 200; |
|
||||
context.Response.Headers.Add("Content-Type", "application/json"); |
|
||||
|
|
||||
if (results.Any(x => !x.Result.IsHealthy)) |
|
||||
{ |
|
||||
context.Response.StatusCode = 503; |
|
||||
} |
|
||||
|
|
||||
var response = results.ToDictionary(x => x.Name, x => x.Result); |
|
||||
|
|
||||
var json = serializer.Serialize(new { status = response }); |
|
||||
|
|
||||
await context.Response.WriteAsync(json); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
await next(context); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private bool CanUseCheck(IHealthCheck check) |
|
||||
{ |
|
||||
return scopes.Count == 0 || check.Scopes.Intersect(scopes).Any(); |
|
||||
} |
|
||||
|
|
||||
private bool CanServeRequest(HttpRequest request) |
|
||||
{ |
|
||||
return HttpMethods.IsGet(request.Method) && (request.Path == "/" || string.IsNullOrEmpty(request.Path)); |
|
||||
} |
|
||||
|
|
||||
private static List<string> SplitScopes(string scopes) |
|
||||
{ |
|
||||
return scopes.Split(",").Where(x => x != "*").ToList(); |
|
||||
} |
|
||||
|
|
||||
private static string GetName(IHealthCheck check) |
|
||||
{ |
|
||||
var name = check.GetType().Name.ToCamelCase(); |
|
||||
|
|
||||
if (name.EndsWith(Suffix, StringComparison.OrdinalIgnoreCase)) |
|
||||
{ |
|
||||
name = name.Substring(0, name.Length - Suffix.Length); |
|
||||
} |
|
||||
|
|
||||
return name; |
|
||||
} |
|
||||
|
|
||||
private async Task<(string Name, HealthCheckResult Result)> MakeHealthCheckAsync(string name, IHealthCheck check, CancellationToken ct) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
var result = await check.CheckHealthAsync(ct); |
|
||||
|
|
||||
return (name, result); |
|
||||
} |
|
||||
catch |
|
||||
{ |
|
||||
return (name, new HealthCheckResult(false)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue