diff --git a/src/ServicePulse.Core/ServicePulse.Core.csproj b/src/ServicePulse.Core/ServicePulse.Core.csproj
new file mode 100644
index 0000000000..722e6b9136
--- /dev/null
+++ b/src/ServicePulse.Core/ServicePulse.Core.csproj
@@ -0,0 +1,24 @@
+
+
+
+ net8.0
+ enable
+ enable
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/ServicePulse.Core/ServicePulseExtensions.cs b/src/ServicePulse.Core/ServicePulseExtensions.cs
new file mode 100644
index 0000000000..058cc1104a
--- /dev/null
+++ b/src/ServicePulse.Core/ServicePulseExtensions.cs
@@ -0,0 +1,80 @@
+namespace ServicePulse;
+
+using System.Net.Mime;
+using System.Reflection;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.FileProviders;
+
+///
+/// Extension methods for hosting a ServicePulse instance.
+///
+public static class ServicePulseExtensions
+{
+ ///
+ /// Adds ServicePulse static file hosting to the application builder.
+ ///
+ public static IApplicationBuilder UseServicePulse(this IApplicationBuilder app, IFileProvider? overrideFileProvider = null)
+ {
+ var embeddedFileProvider = new ManifestEmbeddedFileProvider(typeof(ServicePulseExtensions).Assembly, "wwwroot");
+
+ IFileProvider fileProvider = overrideFileProvider is null
+ ? embeddedFileProvider
+ : new CompositeFileProvider(overrideFileProvider, embeddedFileProvider);
+
+ return app
+ .UseDefaultFiles(new DefaultFilesOptions
+ {
+ FileProvider = fileProvider
+ })
+ .UseStaticFiles(new StaticFileOptions
+ {
+ FileProvider = fileProvider
+ });
+ }
+
+ ///
+ /// Maps the ServicePulse constants endpoint.
+ /// Used to pass settings to the frontend application.
+ ///
+ public static IEndpointRouteBuilder MapServicePulseConstants(this IEndpointRouteBuilder app, ServicePulseSettings settings)
+ {
+ var constantsFile = GetConstantsFileContents(settings);
+
+ app.MapGet("/js/app.constants.js", (HttpContext context) =>
+ {
+ context.Response.ContentType = MediaTypeNames.Text.JavaScript;
+ return constantsFile;
+ });
+
+ return app;
+ }
+
+ static string GetConstantsFileContents(ServicePulseSettings settings)
+ => $$"""
+ window.defaultConfig = {
+ default_route: '{{settings.DefaultRoute}}',
+ version: '{{GetVersionInformation()}}',
+ service_control_url: '{{settings.ServiceControlUrl}}',
+ monitoring_urls: ['{{settings.MonitoringUrl ?? "!"}}'],
+ showPendingRetry: {{(settings.ShowPendingRetry ? "true" : "false")}},
+ }
+ """;
+ static string GetVersionInformation()
+ {
+ var majorMinorPatch = "0.0.0";
+
+ var attributes = typeof(ServicePulseExtensions).Assembly.GetCustomAttributes();
+
+ foreach (var attribute in attributes)
+ {
+ if (attribute.Key == "MajorMinorPatch")
+ {
+ majorMinorPatch = attribute.Value ?? "0.0.0";
+ }
+ }
+
+ return majorMinorPatch;
+ }
+}
\ No newline at end of file
diff --git a/src/ServicePulse/Settings.cs b/src/ServicePulse.Core/ServicePulseSettings.cs
similarity index 69%
rename from src/ServicePulse/Settings.cs
rename to src/ServicePulse.Core/ServicePulseSettings.cs
index 51717b5cbe..4ed984305b 100644
--- a/src/ServicePulse/Settings.cs
+++ b/src/ServicePulse.Core/ServicePulseSettings.cs
@@ -2,19 +2,35 @@
using System.Text.Json;
-class Settings
+///
+/// The runtime settings of a ServicePulse instance.
+///
+public record ServicePulseSettings
{
- public required Uri ServiceControlUri { get; init; }
+ ///
+ /// The default route to navigate to.
+ ///
+ public required string DefaultRoute { get; init; }
- public required Uri? MonitoringUri { get; init; }
+ ///
+ /// The location of the ServiceControl API.
+ ///
+ public required string ServiceControlUrl { get; init; }
- public required string DefaultRoute { get; init; }
+ ///
+ /// The location of the ServiceControl Monitoring API.
+ ///
+ public required string? MonitoringUrl { get; init; }
+ ///
+ /// Show the pending retry tab.
+ ///
public required bool ShowPendingRetry { get; init; }
- public required bool EnableReverseProxy { get; init; }
-
- public static Settings GetFromEnvironmentVariables()
+ ///
+ /// Loads the settings from environment variables.
+ ///
+ public static ServicePulseSettings GetFromEnvironmentVariables()
{
var serviceControlUrl = Environment.GetEnvironmentVariable("SERVICECONTROL_URL") ?? "http://localhost:33333/api/";
@@ -43,20 +59,12 @@ public static Settings GetFromEnvironmentVariables()
var showPendingRetryValue = Environment.GetEnvironmentVariable("SHOW_PENDING_RETRY");
bool.TryParse(showPendingRetryValue, out var showPendingRetry);
- var enableReverseProxyValue = Environment.GetEnvironmentVariable("ENABLE_REVERSE_PROXY");
-
- if (!bool.TryParse(enableReverseProxyValue, out var enableReverseProxy))
- {
- enableReverseProxy = true;
- }
-
- return new Settings
+ return new()
{
- ServiceControlUri = serviceControlUri,
- MonitoringUri = monitoringUri,
+ ServiceControlUrl = serviceControlUri.ToString(),
+ MonitoringUrl = monitoringUri?.ToString(),
DefaultRoute = defaultRoute,
- ShowPendingRetry = showPendingRetry,
- EnableReverseProxy = enableReverseProxy
+ ShowPendingRetry = showPendingRetry
};
}
@@ -95,4 +103,4 @@ class MonitoringUrls
{
public string[] Addresses { get; set; } = [];
}
-}
+}
\ No newline at end of file
diff --git a/src/ServicePulse.sln b/src/ServicePulse.sln
index 2d35b6f089..17984d80a0 100644
--- a/src/ServicePulse.sln
+++ b/src/ServicePulse.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.1.32319.34
+# Visual Studio Version 18
+VisualStudioVersion = 18.1.11312.151 d18.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServicePulse.Host", "ServicePulse.Host\ServicePulse.Host.csproj", "{D120B791-BD1B-4E06-B4E1-69801A73209B}"
EndProject
@@ -36,6 +36,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePulse", "ServicePulse\ServicePulse.csproj", "{084808CF-4B93-4097-BFA1-2604AA7B4594}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePulse.Core", "ServicePulse.Core\ServicePulse.Core.csproj", "{51B99A95-FD20-4B5F-A460-0A1D589BE9F9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -88,6 +90,10 @@ Global
{084808CF-4B93-4097-BFA1-2604AA7B4594}.Debug|Any CPU.Build.0 = Debug|Any CPU
{084808CF-4B93-4097-BFA1-2604AA7B4594}.Release|Any CPU.ActiveCfg = Release|Any CPU
{084808CF-4B93-4097-BFA1-2604AA7B4594}.Release|Any CPU.Build.0 = Release|Any CPU
+ {51B99A95-FD20-4B5F-A460-0A1D589BE9F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {51B99A95-FD20-4B5F-A460-0A1D589BE9F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {51B99A95-FD20-4B5F-A460-0A1D589BE9F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {51B99A95-FD20-4B5F-A460-0A1D589BE9F9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/ServicePulse/ConstantsFile.cs b/src/ServicePulse/ConstantsFile.cs
deleted file mode 100644
index 9ab55df879..0000000000
--- a/src/ServicePulse/ConstantsFile.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-namespace ServicePulse;
-
-using System.Reflection;
-
-class ConstantsFile
-{
- public static string GetContent(Settings settings)
- {
- var version = GetVersionInformation();
-
- string serviceControlUrl;
- string monitoringUrl;
-
- if (settings.EnableReverseProxy)
- {
- serviceControlUrl = "/api/";
- monitoringUrl = settings.MonitoringUri == null ? "!" : "/monitoring-api/";
- }
- else
- {
- serviceControlUrl = settings.ServiceControlUri.ToString();
- monitoringUrl = settings.MonitoringUri?.ToString() ?? "!";
- }
-
- var constantsFile = $$"""
-window.defaultConfig = {
- default_route: '{{settings.DefaultRoute}}',
- version: '{{version}}',
- service_control_url: '{{serviceControlUrl}}',
- monitoring_urls: ['{{monitoringUrl}}'],
- showPendingRetry: {{(settings.ShowPendingRetry ? "true" : "false")}},
-}
-""";
-
- return constantsFile;
- }
-
- static string GetVersionInformation()
- {
- var majorMinorPatch = "0.0.0";
-
- var attributes = Assembly.GetExecutingAssembly().GetCustomAttributes();
-
- foreach (var attribute in attributes)
- {
- if (attribute.Key == "MajorMinorPatch")
- {
- majorMinorPatch = attribute.Value ?? "0.0.0";
- }
- }
-
- return majorMinorPatch;
- }
-}
diff --git a/src/ServicePulse/Program.cs b/src/ServicePulse/Program.cs
index a4c304be50..41ae439515 100644
--- a/src/ServicePulse/Program.cs
+++ b/src/ServicePulse/Program.cs
@@ -1,39 +1,24 @@
-using System.Net.Mime;
-using Microsoft.Extensions.FileProviders;
using ServicePulse;
var builder = WebApplication.CreateBuilder(args);
-var settings = Settings.GetFromEnvironmentVariables();
+var hostSettings = ServicePulseHostSettings.GetFromEnvironmentVariables();
+var servicePulseSettings = ServicePulseSettings.GetFromEnvironmentVariables();
-if (settings.EnableReverseProxy)
+if (hostSettings.EnableReverseProxy)
{
- var (routes, clusters) = ReverseProxy.GetConfiguration(settings);
- builder.Services.AddReverseProxy().LoadFromMemory(routes, clusters);
+ builder.Services.AddServicePulseReverseProxy(ref servicePulseSettings);
}
var app = builder.Build();
-var manifestEmbeddedFileProvider = new ManifestEmbeddedFileProvider(typeof(Program).Assembly, "wwwroot");
-var fileProvider = new CompositeFileProvider(builder.Environment.WebRootFileProvider, manifestEmbeddedFileProvider);
+app.UseServicePulse(builder.Environment.ContentRootFileProvider);
-var defaultFilesOptions = new DefaultFilesOptions { FileProvider = fileProvider };
-app.UseDefaultFiles(defaultFilesOptions);
-
-var staticFileOptions = new StaticFileOptions { FileProvider = fileProvider };
-app.UseStaticFiles(staticFileOptions);
-
-if (settings.EnableReverseProxy)
+if (hostSettings.EnableReverseProxy)
{
app.MapReverseProxy();
}
-var constantsFile = ConstantsFile.GetContent(settings);
-
-app.MapGet("/js/app.constants.js", (HttpContext context) =>
-{
- context.Response.ContentType = MediaTypeNames.Text.JavaScript;
- return constantsFile;
-});
+app.MapServicePulseConstants(servicePulseSettings);
app.Run();
diff --git a/src/ServicePulse/ReverseProxy.cs b/src/ServicePulse/ReverseProxy.cs
index 90ccc591d9..6cb4750f3b 100644
--- a/src/ServicePulse/ReverseProxy.cs
+++ b/src/ServicePulse/ReverseProxy.cs
@@ -5,17 +5,25 @@
static class ReverseProxy
{
- public static (List routes, List clusters) GetConfiguration(Settings settings)
+ public static void AddServicePulseReverseProxy(this IServiceCollection services, ref ServicePulseSettings settings)
{
var routes = new List();
var clusters = new List();
+ AddServiceControl(ref settings, routes, clusters);
+ AddMonitoringIfEnabled(ref settings, routes, clusters);
+
+ services.AddReverseProxy().LoadFromMemory(routes, clusters);
+ }
+
+ static void AddServiceControl(ref ServicePulseSettings settings, List routes, List clusters)
+ {
var serviceControlInstance = new ClusterConfig
{
ClusterId = "serviceControlInstance",
Destinations = new Dictionary
{
- { "instance", new DestinationConfig { Address = settings.ServiceControlUri.ToString() } }
+ { "instance", new DestinationConfig { Address = settings.ServiceControlUrl } }
}
};
var serviceControlRoute = new RouteConfig
@@ -31,28 +39,41 @@ public static (List routes, List clusters) GetConfig
clusters.Add(serviceControlInstance);
routes.Add(serviceControlRoute);
- if (settings.MonitoringUri != null)
+ settings = settings with
{
- var monitoringInstance = new ClusterConfig
- {
- ClusterId = "monitoringInstance",
- Destinations = new Dictionary
- {
- { "instance", new DestinationConfig { Address = settings.MonitoringUri.ToString() } }
- }
- };
-
- var monitoringRoute = new RouteConfig
- {
- RouteId = "monitoringRoute",
- ClusterId = nameof(monitoringInstance),
- Match = new RouteMatch { Path = "/monitoring-api/{**catch-all}" }
- }.WithTransformPathRemovePrefix("/monitoring-api");
+ ServiceControlUrl = "/api/"
+ };
+ }
- clusters.Add(monitoringInstance);
- routes.Add(monitoringRoute);
+ static void AddMonitoringIfEnabled(ref ServicePulseSettings settings, List routes, List clusters)
+ {
+ if (settings.MonitoringUrl is null)
+ {
+ return;
}
- return (routes, clusters);
+ var monitoringInstance = new ClusterConfig
+ {
+ ClusterId = "monitoringInstance",
+ Destinations = new Dictionary
+ {
+ { "instance", new DestinationConfig { Address = settings.MonitoringUrl } }
+ }
+ };
+
+ var monitoringRoute = new RouteConfig
+ {
+ RouteId = "monitoringRoute",
+ ClusterId = nameof(monitoringInstance),
+ Match = new RouteMatch { Path = "/monitoring-api/{**catch-all}" }
+ }.WithTransformPathRemovePrefix("/monitoring-api");
+
+ clusters.Add(monitoringInstance);
+ routes.Add(monitoringRoute);
+ settings = settings with
+ {
+ MonitoringUrl = "/monitoring-api/"
+ };
}
+
}
diff --git a/src/ServicePulse/ServicePulse.csproj b/src/ServicePulse/ServicePulse.csproj
index bd2c0b7f43..36ee65b9ec 100644
--- a/src/ServicePulse/ServicePulse.csproj
+++ b/src/ServicePulse/ServicePulse.csproj
@@ -1,21 +1,19 @@
-
+
net8.0
enable
enable
- true
false
-
-
+
diff --git a/src/ServicePulse/ServicePulseHostSettings.cs b/src/ServicePulse/ServicePulseHostSettings.cs
new file mode 100644
index 0000000000..840e43f81e
--- /dev/null
+++ b/src/ServicePulse/ServicePulseHostSettings.cs
@@ -0,0 +1,21 @@
+namespace ServicePulse;
+
+class ServicePulseHostSettings
+{
+ public required bool EnableReverseProxy { get; init; }
+
+ public static ServicePulseHostSettings GetFromEnvironmentVariables()
+ {
+ var enableReverseProxyValue = Environment.GetEnvironmentVariable("ENABLE_REVERSE_PROXY");
+
+ if (!bool.TryParse(enableReverseProxyValue, out var enableReverseProxy))
+ {
+ enableReverseProxy = true;
+ }
+
+ return new ServicePulseHostSettings
+ {
+ EnableReverseProxy = enableReverseProxy
+ };
+ }
+}
\ No newline at end of file