diff --git a/src/WebApp/Pages/Shared/_MonitoringScriptsPartial.cshtml b/src/WebApp/Pages/Shared/_MonitoringScriptsPartial.cshtml
new file mode 100644
index 0000000..cdbf840
--- /dev/null
+++ b/src/WebApp/Pages/Shared/_MonitoringScriptsPartial.cshtml
@@ -0,0 +1,39 @@
+@using WebApp.Platform
+@{
+ // DataDog
+ var ddClientToken = AppSettings.DataDogSettings.ClientToken;
+ var ddApplicationId = AppSettings.DataDogSettings.ApplicationId;
+ const string service = "checkup";
+
+ if (!string.IsNullOrEmpty(ddClientToken) && !string.IsNullOrEmpty(ddApplicationId))
+ {
+
+ }
+}
diff --git a/src/WebApp/Platform/AppSettings.cs b/src/WebApp/Platform/AppSettings.cs
index 23b6cab..68781ee 100644
--- a/src/WebApp/Platform/AppSettings.cs
+++ b/src/WebApp/Platform/AppSettings.cs
@@ -1,9 +1,10 @@
using JetBrains.Annotations;
using ServerCheckupLibrary.Checks;
+using System.Reflection;
namespace WebApp.Platform;
-public static class AppSettings
+internal static class AppSettings
{
public static CheckEmailOptions CheckEmailOptions { get; } = new();
public static CheckDatabaseOptions CheckDatabaseOptions { get; } = new();
@@ -12,6 +13,45 @@ public static class AppSettings
public static CheckDotnetVersionOptions CheckDotnetVersionOptions { get; } = new();
public static string ServerName { get; set; } = string.Empty;
public static DevOptions DevOptions { get; } = new();
+
+ public static string? Version { get; private set; }
+ public static string? SimpleVersion => Version?.Split('+')[0];
+
+ private static string Env { get; } = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "unknown";
+ public static string ShortEnv => Env switch { "Production" => "prod", "Staging" => "uat", _ => "dev" };
+ public static DataDog DataDogSettings { get; } = new();
+
+ public record DataDog
+ {
+ public string? ClientToken { get; [UsedImplicitly] init; }
+ public string? ApplicationId { get; [UsedImplicitly] init; }
+ }
+
+ public static void BindAppSettings(this IHostApplicationBuilder builder)
+ {
+ // Set default timeout for regular expressions.
+ // https://learn.microsoft.com/en-us/dotnet/standard/base-types/best-practices#use-time-out-values
+ AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT", TimeSpan.FromMilliseconds(100));
+
+ Version = GetVersion();
+ ServerName = builder.Configuration.GetValue
(nameof(ServerName)) ?? "Unknown";
+
+ builder.Configuration.GetSection(nameof(CheckEmailOptions)).Bind(CheckEmailOptions);
+ builder.Configuration.GetSection(nameof(CheckDatabaseOptions)).Bind(CheckDatabaseOptions);
+ builder.Configuration.GetSection(nameof(CheckDatabaseEmailOptions)).Bind(CheckDatabaseEmailOptions);
+ builder.Configuration.GetSection(nameof(CheckExternalServiceOptions)).Bind(CheckExternalServiceOptions);
+ builder.Configuration.GetSection(nameof(CheckDotnetVersionOptions)).Bind(CheckDotnetVersionOptions);
+ builder.Configuration.GetSection(nameof(DevOptions)).Bind(DevOptions);
+ builder.Configuration.GetSection(nameof(DataDogSettings)).Bind(DataDogSettings);
+ }
+
+ private static string GetVersion()
+ {
+ var entryAssembly = Assembly.GetEntryAssembly();
+ var segments = (entryAssembly?.GetCustomAttribute()?
+ .InformationalVersion ?? entryAssembly?.GetName().Version?.ToString() ?? "").Split('+');
+ return segments[0] + (segments.Length > 0 ? $"+{segments[1][..Math.Min(7, segments[1].Length)]}" : "");
+ }
}
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
diff --git a/src/WebApp/Platform/AuthenticationServices.cs b/src/WebApp/Platform/AuthenticationServices.cs
new file mode 100644
index 0000000..7d5f058
--- /dev/null
+++ b/src/WebApp/Platform/AuthenticationServices.cs
@@ -0,0 +1,48 @@
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.Cookies;
+using Microsoft.AspNetCore.Authentication.OpenIdConnect;
+using Microsoft.IdentityModel.Protocols.OpenIdConnect;
+using Microsoft.IdentityModel.Tokens;
+
+namespace WebApp.Platform;
+
+public static class AuthenticationServices
+{
+ public static void ConfigureAuthentication(this IHostApplicationBuilder builder)
+ {
+ if (AppSettings.DevOptions.UseLocalAuth)
+ {
+ // When running locally, use a built-in authenticated user.
+ builder.Services
+ .AddAuthentication(LocalAuthenticationHandler.BasicAuthenticationScheme)
+ .AddScheme(
+ LocalAuthenticationHandler.BasicAuthenticationScheme, null);
+ }
+ else
+ {
+ // When running on the server, require an OIDC login provider (configured in the app settings file).
+ builder.Services
+ .AddAuthentication(options =>
+ {
+ options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
+ options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
+ })
+ .AddCookie()
+ .AddOpenIdConnect(configureOptions: options =>
+ {
+ var configSection = builder.Configuration.GetSection("OIDC");
+
+ options.Authority = configSection["Authority"];
+ options.ClientId = configSection["ClientId"];
+ options.ClientSecret = configSection["ClientSecret"];
+ options.CallbackPath = configSection["CallbackPath"];
+
+ options.Scope.Add("email");
+ options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
+ options.ResponseType = OpenIdConnectResponseType.Code;
+ options.MapInboundClaims = false;
+ options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "email" };
+ });
+ }
+ }
+}
diff --git a/src/WebApp/Program.cs b/src/WebApp/Program.cs
index ec6a320..938348b 100644
--- a/src/WebApp/Program.cs
+++ b/src/WebApp/Program.cs
@@ -1,9 +1,3 @@
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.AspNetCore.Authentication.Cookies;
-using Microsoft.AspNetCore.Authentication.OpenIdConnect;
-using Microsoft.IdentityModel.Protocols.OpenIdConnect;
-using Microsoft.IdentityModel.Tokens;
-using ServerCheckupLibrary.Checks;
using ServerCheckupLibrary.Hubs;
using WebApp.Platform;
using ZLogger;
@@ -11,57 +5,17 @@
var builder = WebApplication.CreateBuilder(args);
var isDevelopment = builder.Environment.IsDevelopment();
-// Configure logging
+// Configure logging.
builder.Logging.ClearProviders().AddZLoggerConsole(options => options.UseJsonFormatter());
-// Persist data protection keys
+// Persist data protection keys.
builder.Services.AddDataProtection();
// Bind application settings.
-builder.Configuration.GetSection(nameof(CheckEmailOptions)).Bind(AppSettings.CheckEmailOptions);
-builder.Configuration.GetSection(nameof(CheckDatabaseOptions)).Bind(AppSettings.CheckDatabaseOptions);
-builder.Configuration.GetSection(nameof(CheckDatabaseEmailOptions)).Bind(AppSettings.CheckDatabaseEmailOptions);
-builder.Configuration.GetSection(nameof(CheckExternalServiceOptions)).Bind(AppSettings.CheckExternalServiceOptions);
-builder.Configuration.GetSection(nameof(CheckDotnetVersionOptions)).Bind(AppSettings.CheckDotnetVersionOptions);
-builder.Configuration.GetSection(nameof(DevOptions)).Bind(AppSettings.DevOptions);
-AppSettings.ServerName = builder.Configuration.GetValue(nameof(AppSettings.ServerName)) ?? "Unknown";
-
-// Configure authentication.
-if (AppSettings.DevOptions.UseLocalAuth)
-{
- // When running locally, use a built-in authenticated user.
- builder.Services
- .AddAuthentication(LocalAuthenticationHandler.BasicAuthenticationScheme)
- .AddScheme(
- LocalAuthenticationHandler.BasicAuthenticationScheme, null);
-}
-else
-{
- // When running on the server, require an OIDC login provider (configured in the appsettings file).
- builder.Services
- .AddAuthentication(options =>
- {
- options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
- options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
- })
- .AddCookie()
- .AddOpenIdConnect(configureOptions: options =>
- {
- var configSection = builder.Configuration.GetSection("OIDC");
-
- options.Authority = configSection["Authority"];
- options.ClientId = configSection["ClientId"];
- options.ClientSecret = configSection["ClientSecret"];
- options.CallbackPath = configSection["CallbackPath"];
-
- options.Scope.Add("email");
- options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
- options.ResponseType = OpenIdConnectResponseType.Code;
- options.MapInboundClaims = false;
- options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "email" };
- });
-}
+builder.BindAppSettings();
+// Configure authentication and authorization.
+builder.ConfigureAuthentication();
builder.Services.AddAuthorization();
// Add SignalR
diff --git a/src/WebApp/appsettings-example.json b/src/WebApp/appsettings-example.json
index a3c51d8..2818122 100644
--- a/src/WebApp/appsettings-example.json
+++ b/src/WebApp/appsettings-example.json
@@ -68,8 +68,12 @@
"Instance": "https://login.microsoftonline.com/",
"Domain": "qualified.domain.name",
"TenantId": "00000000-0000-0000-0000-000000000000",
- "ClientId": "00000000-0000-0000-00000000000000000",
+ "ClientId": "00000000-0000-0000-0000-000000000000",
"CallbackPath": "/signin-oidc"
},
+ "DataDogSettings": {
+ "ClientToken": "pub0000000000000000000",
+ "ApplicationId": "00000000-0000-0000-0000-000000000000"
+ },
"AllowedHosts": "*"
}
diff --git a/src/WebApp/appsettings.json b/src/WebApp/appsettings.json
index 6874e56..d5cc9cd 100644
--- a/src/WebApp/appsettings.json
+++ b/src/WebApp/appsettings.json
@@ -32,6 +32,10 @@
"ClientId": "",
"CallbackPath": "/signin-oidc"
},
+ "DataDogSettings": {
+ "ClientToken": "",
+ "ApplicationId": ""
+ },
"Logging": {
"LogLevel": {
"Default": "Information",