Skip to content

Commit 445d942

Browse files
committed
feature: HealthChecks
1 parent 0771cd1 commit 445d942

File tree

11 files changed

+328
-0
lines changed

11 files changed

+328
-0
lines changed

src/TestSite.10/appsettings.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@
1111
}
1212
},
1313
"Umbraco": {
14+
"Storage": {
15+
"B2": {
16+
"Media": {
17+
"BucketName": "media",
18+
"ServiceUrl": "http://localhost:4566",
19+
"UseAccelerateEndpoint": false,
20+
"Credentials": {
21+
"ApplicationKey": "test",
22+
"KeyId": "test"
23+
}
24+
}
25+
}
26+
},
1427
"CMS": {
1528
"Global": {
1629
"SanitizeTinyMce": true,

src/TestSite.12/appsettings.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@
1111
}
1212
},
1313
"Umbraco": {
14+
"Storage": {
15+
"B2": {
16+
"Media": {
17+
"BucketName": "media",
18+
"ServiceUrl": "http://localhost:4566",
19+
"UseAccelerateEndpoint": false,
20+
"Credentials": {
21+
"ApplicationKey": "test",
22+
"KeyId": "test"
23+
}
24+
}
25+
}
26+
},
1427
"CMS": {
1528
"Global": {
1629
"SanitizeTinyMce": true,

src/TestSite.13/appsettings.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@
1111
}
1212
},
1313
"Umbraco": {
14+
"Storage": {
15+
"B2": {
16+
"Media": {
17+
"BucketName": "media",
18+
"ServiceUrl": "http://localhost:4566",
19+
"UseAccelerateEndpoint": false,
20+
"Credentials": {
21+
"ApplicationKey": "test",
22+
"KeyId": "test"
23+
}
24+
}
25+
}
26+
},
1427
"CMS": {
1528
"Global": {
1629
"SanitizeTinyMce": true,
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
namespace Umbraco.Community.FileSystemProviders.B2;
2+
3+
public class Constants
4+
{
5+
public const string Section = "Umbraco:Storage:B2:Media";
6+
7+
public class HealthChecks
8+
{
9+
public class BucketName
10+
{
11+
public const string Id = "D4D3D3D3-4D3D-4D3D-4D3D-4D3D3D3D3D3D";
12+
public const string Name = "B2 Bucket Name";
13+
public const string Description = "Checks if the B2 bucket name is set.";
14+
public const string ItemPath = $"{Section}:BucketName";
15+
public const string ReadMoreLink = "https://our.umbraco.com/Documentation/Extending/FileSystemProviders/B2/";
16+
}
17+
18+
public class ApplicationKey
19+
{
20+
public const string Id = "C4D3D3D3-4D3D-4D3D-4D3D-4D3D3D3D3D3D";
21+
public const string Name = "B2 Secret Key";
22+
public const string Description = "Checks if the B2 secret key is set.";
23+
public const string ItemPath = $"{Section}:Credentials:KeyId";
24+
public const string ReadMoreLink = "https://our.umbraco.com/Documentation/Extending/FileSystemProviders/B2/";
25+
}
26+
27+
public class KeyId
28+
{
29+
public const string Id = "B4D3D3D3-4D3D-4D3D-4D3D-4D3D3D3D3D3D";
30+
public const string Name = "B2 Access Key";
31+
public const string Description = "Checks if the B2 access key is set.";
32+
public const string ItemPath = $"{Section}:Credentials:ApplicationKey";
33+
public const string ReadMoreLink = "https://our.umbraco.com/Documentation/Extending/FileSystemProviders/B2/";
34+
}
35+
36+
public class ServiceUrl
37+
{
38+
public const string Id = "A4D3D3D3-4D3D-4D3D-4D3D-4D3D3D3D3D3D";
39+
public const string Name = "B2 Service Url";
40+
public const string Description = "Checks if the B2 service URL is set.";
41+
public const string ItemPath = $"{Section}:ServiceUrl";
42+
public const string ReadMoreLink = "https://our.umbraco.com/Documentation/Extending/FileSystemProviders/B2/";
43+
}
44+
45+
public class UseAccelerateEndpoint
46+
{
47+
public const string Id = "9D3D3D3D-4D3D-4D3D-4D3D-4D3D3D3D3D3D";
48+
public const string Name = "B2 Use Accelerate Endpoint";
49+
public const string Description = "Checks if the B2 use accelerate endpoint is set.";
50+
public const string ItemPath = $"{Section}:UseAccelerateEndpoint";
51+
public const string ReadMoreLink = "https://our.umbraco.com/Documentation/Extending/FileSystemProviders/B2/";
52+
}
53+
54+
public class Api
55+
{
56+
public const string Id = "8D3D3D3D-4D3D-4D3D-4D3D-4D3D3D3D3D3D";
57+
public const string Name = "B2 API";
58+
public const string Description = "Checks if the B2 API is healthy.";
59+
public const string ReadMoreLink = "https://our.umbraco.com/Documentation/Extending/FileSystemProviders/B2/";
60+
}
61+
62+
public class Groups
63+
{
64+
public const string ApiClient = "B2 API";
65+
public const string Configuration = "B2 Configuration";
66+
}
67+
}
68+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using Microsoft.Extensions.Logging;
2+
using Umbraco.Cms.Core.HealthChecks;
3+
using Umbraco.Cms.Core.IO;
4+
using Umbraco.Cms.Core.Services;
5+
using Umbraco.Extensions;
6+
7+
namespace Umbraco.Community.FileSystemProviders.B2.HealthChecks;
8+
9+
[HealthCheck(
10+
Constants.HealthChecks.Api.Id,
11+
Constants.HealthChecks.Api.Name,
12+
Description = Constants.HealthChecks.Api.Description,
13+
Group = Constants.HealthChecks.Groups.ApiClient)]
14+
internal class ApiHealthCheck(B2FileSystemProvider mediaFileManager, ILogger<ApiHealthCheck> logger, ILocalizedTextService textService) : HealthCheck
15+
{
16+
private readonly ILogger _logger = logger;
17+
18+
public override async Task<IEnumerable<HealthCheckStatus>> GetStatus()
19+
{
20+
if (!TryGetFileSystem(mediaFileManager, out var fs) || fs is null)
21+
{
22+
return new List<HealthCheckStatus>
23+
{
24+
new(textService.Localize("healthcheck", "b2FileSystemNotAvailable"))
25+
{
26+
Description = null,
27+
View = null,
28+
ResultType = StatusResultType.Error,
29+
ReadMoreLink = Constants.HealthChecks.Api.ReadMoreLink
30+
}
31+
};
32+
}
33+
34+
try
35+
{
36+
var files = fs.GetDirectories("");
37+
return new List<HealthCheckStatus>
38+
{
39+
new(textService.Localize("healthcheck", "b2FileSystemAvailable"))
40+
{
41+
Description = textService.Localize("healthcheck", "b2FileSystemMediaFolderCount", new[] { files.Count().ToString() }),
42+
43+
View = null,
44+
ResultType = StatusResultType.Success
45+
}
46+
};
47+
}
48+
catch (Exception ex)
49+
{
50+
return new List<HealthCheckStatus>
51+
{
52+
new(textService.Localize("healthcheck", "b2FileSystemError"))
53+
{
54+
Description = $"{ex.Message}",
55+
View = null,
56+
ResultType = StatusResultType.Error,
57+
ReadMoreLink = Constants.HealthChecks.Api.ReadMoreLink
58+
}
59+
};
60+
}
61+
}
62+
63+
private bool TryGetFileSystem(B2FileSystemProvider mediaFileManager, out IFileSystem? fs)
64+
{
65+
fs = null;
66+
try
67+
{
68+
if (mediaFileManager.GetFileSystem("Media") is IFileSystem fss)
69+
{
70+
fs = fss;
71+
return true;
72+
}
73+
}
74+
catch (Exception ex)
75+
{
76+
_logger.LogError(ex, "Failed to get the media file system");
77+
}
78+
79+
return false;
80+
}
81+
82+
public override HealthCheckStatus ExecuteAction(HealthCheckAction action) => new("Action not supported")
83+
{
84+
ResultType = StatusResultType.Error
85+
};
86+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Microsoft.Extensions.Options;
2+
using Umbraco.Cms.Core.HealthChecks;
3+
using Umbraco.Cms.Core.Services;
4+
using Umbraco.Community.FileSystemProviders.B2.Models;
5+
6+
namespace Umbraco.Community.FileSystemProviders.B2.HealthChecks;
7+
8+
[HealthCheck(
9+
Constants.HealthChecks.ApplicationKey.Id,
10+
Constants.HealthChecks.ApplicationKey.Name,
11+
Description = Constants.HealthChecks.ApplicationKey.Description,
12+
Group = Constants.HealthChecks.Groups.Configuration)]
13+
internal class ApplicationKeyHealthCheck(ILocalizedTextService textService, IOptions<B2Options> options) : ConfigurationNotNullHealthcheck<B2Options>(textService, options)
14+
{
15+
public override string ItemPath => Constants.HealthChecks.ApplicationKey.ItemPath;
16+
public override string ReadMoreLink => Constants.HealthChecks.ApplicationKey.ReadMoreLink;
17+
public override string CurrentValue => Options.Credentials?.ApplicationKey ?? string.Empty;
18+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using Microsoft.Extensions.Hosting;
2+
using Microsoft.Extensions.Options;
3+
using Umbraco.Cms.Core.HealthChecks;
4+
using Umbraco.Cms.Core.Services;
5+
using Umbraco.Community.FileSystemProviders.B2.Models;
6+
7+
namespace Umbraco.Community.FileSystemProviders.B2.HealthChecks;
8+
9+
[HealthCheck(
10+
Constants.HealthChecks.BucketName.Id,
11+
Constants.HealthChecks.BucketName.Name,
12+
Description = Constants.HealthChecks.BucketName.Description,
13+
Group = Constants.HealthChecks.Groups.Configuration)]
14+
internal class BucketNameHealthCheck(ILocalizedTextService textService, IOptions<B2Options> options, IHostEnvironment host) : ConfigurationNotNullHealthcheck<B2Options>(textService, options)
15+
{
16+
public override string ItemPath => Constants.HealthChecks.BucketName.ItemPath;
17+
public override string ReadMoreLink => Constants.HealthChecks.BucketName.ReadMoreLink;
18+
public override string CurrentValue => Options.BucketName ?? string.Empty;
19+
protected override string Recommended => $"media-{host.EnvironmentName}".ToLowerInvariant();
20+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using Microsoft.Extensions.Options;
2+
using Umbraco.Cms.Core.HealthChecks;
3+
using Umbraco.Cms.Core.HealthChecks.Checks;
4+
using Umbraco.Cms.Core.Services;
5+
using Umbraco.Extensions;
6+
7+
namespace Umbraco.Community.FileSystemProviders.B2.HealthChecks;
8+
9+
/// <summary>
10+
/// Ensure a lang file exists with the area "healthcheck" and keys:<br/>
11+
/// aliasCheckSuccessMessage<br/>
12+
/// aliasCheckErrorMessage<br/>
13+
/// aliasCheckErrorMessageWithRecommendedValue<br/>
14+
/// alias = The name of <see cref="T"/> e.g MyOptions = myOptionsCheckSuccessMessage<br/>
15+
/// %0% = Current value<br/>
16+
/// %1% = Item path<br/>
17+
/// %2% = Recommended value (CheckErrorMessageWithRecommendedValue only)<br/>
18+
/// </summary>
19+
public abstract class ConfigurationNotNullHealthcheck<T>(ILocalizedTextService textService, IOptions<T> options) : AbstractSettingsCheck(textService) where T : class
20+
{
21+
private readonly string _prefix = typeof(T).Name.ToFirstLowerInvariant();
22+
protected T Options => options.Value;
23+
public override ValueComparisonType ValueComparisonType => ValueComparisonType.ShouldNotEqual;
24+
public override string CheckSuccessMessage => LocalizedTextService.Localize("healthcheck", $"{_prefix}CheckSuccessMessage", new[] { CurrentValueDisplay, ItemPaths });
25+
26+
public override string CheckErrorMessage =>
27+
Recommended.IsNullOrWhiteSpace()
28+
? LocalizedTextService.Localize("healthcheck", $"{_prefix}CheckErrorMessage", new[] { CurrentValueDisplay, ItemPaths })
29+
: LocalizedTextService.Localize("healthcheck", $"{_prefix}CheckErrorMessageWithRecommendedValue", new[] { CurrentValueDisplay, ItemPaths, Recommended });
30+
31+
private string CurrentValueDisplay => CurrentValue.IsNullOrWhiteSpace() ? "<em>undefined</em>" : $"<code>{CurrentValue}</code>";
32+
private string ItemPaths => $"<code>{ItemPath}</code> | <code>{ItemPath.Replace(":", "__")}</code>";
33+
34+
public override IEnumerable<AcceptableConfiguration> Values => new List<AcceptableConfiguration>
35+
{
36+
new()
37+
{
38+
Value = null,
39+
IsRecommended = false
40+
},
41+
new()
42+
{
43+
Value = "",
44+
IsRecommended = false
45+
}
46+
};
47+
48+
protected virtual string? Recommended => null;
49+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Microsoft.Extensions.Options;
2+
using Umbraco.Cms.Core.HealthChecks;
3+
using Umbraco.Cms.Core.Services;
4+
using Umbraco.Community.FileSystemProviders.B2.Models;
5+
6+
namespace Umbraco.Community.FileSystemProviders.B2.HealthChecks;
7+
8+
[HealthCheck(
9+
Constants.HealthChecks.KeyId.Id,
10+
Constants.HealthChecks.KeyId.Name,
11+
Description = Constants.HealthChecks.KeyId.Description,
12+
Group = Constants.HealthChecks.Groups.Configuration)]
13+
internal class KeyIdHealthCheck(ILocalizedTextService textService, IOptions<B2Options> options) : ConfigurationNotNullHealthcheck<B2Options>(textService, options)
14+
{
15+
public override string ItemPath => Constants.HealthChecks.KeyId.ItemPath;
16+
public override string ReadMoreLink => Constants.HealthChecks.KeyId.ReadMoreLink;
17+
public override string CurrentValue => Options.Credentials?.KeyId ?? string.Empty;
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Microsoft.Extensions.Options;
2+
using Umbraco.Cms.Core.HealthChecks;
3+
using Umbraco.Cms.Core.Services;
4+
using Umbraco.Community.FileSystemProviders.B2.Models;
5+
6+
namespace Umbraco.Community.FileSystemProviders.B2.HealthChecks;
7+
8+
[HealthCheck(
9+
Constants.HealthChecks.ServiceUrl.Id,
10+
Constants.HealthChecks.ServiceUrl.Name,
11+
Description = Constants.HealthChecks.ServiceUrl.Description,
12+
Group = Constants.HealthChecks.Groups.Configuration)]
13+
internal class ServiceUrlHealthCheck(ILocalizedTextService textService, IOptions<B2Options> options) : ConfigurationNotNullHealthcheck<B2Options>(textService, options)
14+
{
15+
public override string ItemPath => Constants.HealthChecks.ServiceUrl.ItemPath;
16+
public override string ReadMoreLink => Constants.HealthChecks.ServiceUrl.ReadMoreLink;
17+
public override string CurrentValue => Options.ServiceUrl ?? string.Empty;
18+
}

0 commit comments

Comments
 (0)