Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ jobs:
$nugetsCount = (Get-ChildItem -Recurse -File nugets).Count

$expectedAssetsCount = 1
$expectedNugetsCount = 1
$expectedNugetsCount = 2

if ($assetsCount -ne $expectedAssetsCount)
{
Expand Down
1 change: 1 addition & 0 deletions src/Frontend/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ declare global {
service_control_url: string;
monitoring_urls: string[];
showPendingRetry: boolean;
isEmbedded?: boolean;
};
}
}
1 change: 1 addition & 0 deletions src/Frontend/public/js/app.constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ window.defaultConfig = {
service_control_url: 'http://localhost:33333/api/',
monitoring_urls: ['http://localhost:33633/'],
showPendingRetry: false,
isEmbedded: false,
};
6 changes: 4 additions & 2 deletions src/Frontend/src/components/PageFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const environment = environmentAndVersionsStore.environment;
const licenseStore = useLicenseStore();
const { licenseStatus, license } = licenseStore;
const isMonitoringEnabled = monitoringClient.isMonitoringEnabled;
const isEmbedded = window.defaultConfig.isEmbedded;

const scAddressTooltip = computed(() => {
return `ServiceControl URL ${serviceControlClient.url}`;
Expand All @@ -44,8 +45,9 @@ const { configuration } = storeToRefs(configurationStore);
<RouterLink :to="routeLinks.configuration.endpointConnection.link">Connect new endpoint</RouterLink>
</span>

<span v-if="!newVersions.newSPVersion.newspversion && environment.sp_version"> ServicePulse v{{ environment.sp_version }} </span>
<span v-if="newVersions.newSPVersion.newspversion && environment.sp_version">
<span v-if="isEmbedded"> ServicePulse: Embedded </span>
<span v-if="!isEmbedded && !newVersions.newSPVersion.newspversion && environment.sp_version"> ServicePulse v{{ environment.sp_version }} </span>
<span v-if="!isEmbedded && newVersions.newSPVersion.newspversion && environment.sp_version">
ServicePulse v{{ environment.sp_version }} (<FAIcon v-if="newVersions.newSPVersion.newspversionnumber" class="footer-icon fake-link" :icon="faArrowTurnUp" />
<a :href="newVersions.newSPVersion.newspversionlink" target="_blank">v{{ newVersions.newSPVersion.newspversionnumber }} available</a>)
</span>
Expand Down
21 changes: 14 additions & 7 deletions src/Frontend/src/components/configuration/PlatformConnections.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const serviceControlValid = ref<boolean | null>(null);
const testingMonitoring = ref(false);
const monitoringValid = ref<boolean | null>(null);
const connectionSaved = ref<boolean | null>(null);
const isEmbedded = window.defaultConfig.isEmbedded;

async function testServiceControlUrl() {
if (localServiceControlUrl.value) {
Expand Down Expand Up @@ -64,10 +65,15 @@ function saveConnections() {
}

function updateServiceControlUrls() {
if (!localServiceControlUrl.value) {
throw new Error("ServiceControl URL is mandatory");
} else if (!localServiceControlUrl.value.endsWith("/")) {
localServiceControlUrl.value += "/";
const params = new URLSearchParams();

if (!isEmbedded) {
if (!localServiceControlUrl.value) {
throw new Error("ServiceControl URL is mandatory");
} else if (!localServiceControlUrl.value.endsWith("/")) {
localServiceControlUrl.value += "/";
}
params.set("scu", localServiceControlUrl.value);
}

if (!localMonitoringUrl.value) {
Expand All @@ -76,8 +82,6 @@ function updateServiceControlUrls() {
localMonitoringUrl.value += "/";
}

const params = new URLSearchParams();
params.set("scu", localServiceControlUrl.value);
params.set("mu", localMonitoringUrl.value);
window.location.search = `?${params.toString()}`;
}
Expand All @@ -94,11 +98,14 @@ function updateServiceControlUrls() {
<div class="col-7 form-group">
<label for="serviceControlUrl">
CONNECTION URL
<template v-if="isEmbedded">
<span>(EMBEDDED)</span>
</template>
<template v-if="connectionState.unableToConnect">
<span class="failed-validation"><FAIcon :icon="faExclamationTriangle" /> Unable to connect </span>
</template>
</label>
<input type="text" id="serviceControlUrl" name="serviceControlUrl" v-model="localServiceControlUrl" class="form-control" style="color: #000" required />
<input type="text" id="serviceControlUrl" name="serviceControlUrl" v-model="localServiceControlUrl" class="form-control" style="color: #000" required :disabled="isEmbedded" />
</div>

<div class="col-5 no-side-padding">
Expand Down
4 changes: 4 additions & 0 deletions src/Frontend/src/components/serviceControlClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ class ServiceControlClient {
}

private getUrl() {
if (window.defaultConfig && window.defaultConfig.isEmbedded && window.defaultConfig.service_control_url && window.defaultConfig.service_control_url.length) {
return window.defaultConfig.service_control_url;
}

const searchParams = new URLSearchParams(window.location.search);
const scu = searchParams.get("scu");
const existingScu = window.localStorage.getItem("scu");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,18 @@

class ConstantsFile
{
public static string GetContent(Settings settings)
public static string GetContent(ServicePulseSettings 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}}'],
service_control_url: '{{settings.ServiceControlUrl}}',
monitoring_urls: ['{{settings.MonitoringUrl ?? "!"}}'],
showPendingRetry: {{(settings.ShowPendingRetry ? "true" : "false")}},
isEmbedded: {{(settings.IsEmbedded ? "true" : "false")}}
}
""";

Expand All @@ -39,7 +26,7 @@ static string GetVersionInformation()
{
var majorMinorPatch = "0.0.0";

var attributes = Assembly.GetExecutingAssembly().GetCustomAttributes<AssemblyMetadataAttribute>();
var attributes = typeof(ConstantsFile).Assembly.GetCustomAttributes<AssemblyMetadataAttribute>();

foreach (var attribute in attributes)
{
Expand Down
23 changes: 23 additions & 0 deletions src/ServicePulse.Core/ServicePulse.Core.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.17" AutomaticVersionRange="false" />
<PackageReference Include="Particular.Packaging" Version="4.5.0" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="..\Frontend\dist\**\*" Exclude="..\Frontend\dist\js\**\*" LinkBase="wwwroot" />
</ItemGroup>

</Project>
36 changes: 36 additions & 0 deletions src/ServicePulse.Core/ServicePulseHostingExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Net.Mime;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.FileProviders;
using ServicePulse;

/// <summary>
/// Extensions for hosting ServicePulse within a WebApplication.
/// </summary>
public static class ServicePulseHostingExtensions
{
/// <summary>
/// Adds ServicePulse static file serving and configuration endpoint to the WebApplication.
/// </summary>
public static void UseServicePulse(this WebApplication app, ServicePulseSettings settings, IFileProvider? overrideFileProvider = null)
{
var manifestEmbeddedFileProvider = new ManifestEmbeddedFileProvider(typeof(ServicePulseHostingExtensions).Assembly, "wwwroot");
IFileProvider fileProvider = overrideFileProvider is null
? manifestEmbeddedFileProvider
: new CompositeFileProvider(overrideFileProvider, manifestEmbeddedFileProvider);

var defaultFilesOptions = new DefaultFilesOptions { FileProvider = fileProvider };
app.UseDefaultFiles(defaultFilesOptions);

var staticFileOptions = new StaticFileOptions { FileProvider = fileProvider };
app.UseStaticFiles(staticFileOptions);

var constantsFile = ConstantsFile.GetContent(settings);

app.MapGet("/js/app.constants.js", (HttpContext context) =>
{
context.Response.ContentType = MediaTypeNames.Text.JavaScript;
return constantsFile;
});
}
}
112 changes: 112 additions & 0 deletions src/ServicePulse.Core/ServicePulseSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
namespace ServicePulse;

using System.Text.Json;

/// <summary>
/// Application Settings for ServicePulse.
/// </summary>
public record ServicePulseSettings
{
/// <summary>
/// The location of the ServiceControl API.
/// </summary>
public required string ServiceControlUrl { get; init; }

/// <summary>
/// The location of the ServiceControl Monitoring API.
/// </summary>
public required string? MonitoringUrl { get; init; }

/// <summary>
/// The default route to navigate to from the root.
/// </summary>
public required string DefaultRoute { get; init; }

/// <summary>
/// Flag to enable the pending retry feature.
/// </summary>
public required bool ShowPendingRetry { get; init; }

/// <summary>
/// Flag to indicate if ServicePulse is running in embedded mode.
/// </summary>
public required bool IsEmbedded { get; init; }

/// <summary>
/// Loads the settings from environment variables.
/// </summary>
public static ServicePulseSettings GetFromEnvironmentVariables()
{
var serviceControlUrl = Environment.GetEnvironmentVariable("SERVICECONTROL_URL") ?? "http://localhost:33333/api/";

if (!serviceControlUrl.EndsWith("/", StringComparison.Ordinal))
{
serviceControlUrl += "/";
}

if (!serviceControlUrl.EndsWith("api/", StringComparison.Ordinal))
{
serviceControlUrl += "api/";
}

var serviceControlUri = new Uri(serviceControlUrl);

var monitoringUrls = ParseLegacyMonitoringValue(Environment.GetEnvironmentVariable("MONITORING_URLS"));
var monitoringUrl = Environment.GetEnvironmentVariable("MONITORING_URL");

monitoringUrl ??= monitoringUrls;
monitoringUrl ??= "http://localhost:33633/";

var monitoringUri = monitoringUrl == "!" ? null : new Uri(monitoringUrl);

var defaultRoute = Environment.GetEnvironmentVariable("DEFAULT_ROUTE") ?? "/dashboard";

var showPendingRetryValue = Environment.GetEnvironmentVariable("SHOW_PENDING_RETRY");
bool.TryParse(showPendingRetryValue, out var showPendingRetry);

return new ServicePulseSettings
{
ServiceControlUrl = serviceControlUri.ToString(),
MonitoringUrl = monitoringUri?.ToString(),
DefaultRoute = defaultRoute,
ShowPendingRetry = showPendingRetry,
IsEmbedded = false
};
}

static string? ParseLegacyMonitoringValue(string? value)
{
if (value is null)
{
return null;
}

var cleanedValue = value.Replace('\'', '"');
var json = $$"""{"Addresses":{{cleanedValue}}}""";

MonitoringUrls? result;

try
{
result = JsonSerializer.Deserialize<MonitoringUrls>(json);
}
catch (JsonException)
{
return null;
}

var addresses = result?.Addresses;

if (addresses is not null && addresses.Length > 0)
{
return addresses[0];
}

return null;
}

class MonitoringUrls
{
public string[] Addresses { get; set; } = [];
}
}
2 changes: 1 addition & 1 deletion src/ServicePulse.Host.Tests/Owin/StaticMiddlewareTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public async Task Should_find_prefer_constants_file_on_disk_over_embedded_if_bot
}
};
await middleware.Invoke(context);
const long sizeOfFileOnDisk = 215; // this is the /app/js/app.constants.js file
const long sizeOfFileOnDisk = 237; // this is the /app/js/app.constants.js file
Assert.That(context.Response.ContentLength, Is.EqualTo(sizeOfFileOnDisk));
Assert.That(context.Response.ContentType, Is.EqualTo("application/javascript"));
}
Expand Down
10 changes: 8 additions & 2 deletions src/ServicePulse.sln
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -38,6 +38,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePulse", "ServicePuls
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePulse.Tests", "ServicePulse.Tests\ServicePulse.Tests.csproj", "{9B75F526-937E-4B25-A9F6-2862129993EB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePulse.Core", "ServicePulse.Core\ServicePulse.Core.csproj", "{8FCA3827-719C-49B7-A143-E48461D60F3C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -94,6 +96,10 @@ Global
{9B75F526-937E-4B25-A9F6-2862129993EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9B75F526-937E-4B25-A9F6-2862129993EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9B75F526-937E-4B25-A9F6-2862129993EB}.Release|Any CPU.Build.0 = Release|Any CPU
{8FCA3827-719C-49B7-A143-E48461D60F3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8FCA3827-719C-49B7-A143-E48461D60F3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8FCA3827-719C-49B7-A143-E48461D60F3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8FCA3827-719C-49B7-A143-E48461D60F3C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Loading