From cd8cf811641b73c0943fbcc2a34fd297d7aea7dc Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sat, 22 Nov 2025 20:15:03 -0400 Subject: [PATCH 1/6] this probably works --- .../Notifications/NotificationManager.cs | 67 ++++++++++++++++++- .../Notifications/NotificationOptions.cs | 16 ++++- .../ServerManagement/ServerInstance.Actor.cs | 3 + SS14.Watchdog/Startup.cs | 2 +- 4 files changed, 84 insertions(+), 4 deletions(-) diff --git a/SS14.Watchdog/Components/Notifications/NotificationManager.cs b/SS14.Watchdog/Components/Notifications/NotificationManager.cs index 1ce33b7..03a6d82 100644 --- a/SS14.Watchdog/Components/Notifications/NotificationManager.cs +++ b/SS14.Watchdog/Components/Notifications/NotificationManager.cs @@ -1,7 +1,9 @@ using System; using System.Net.Http; +using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json.Serialization; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -16,7 +18,7 @@ public sealed partial class NotificationManager( ILogger logger, IOptions options) { - public const string DiscordHttpClient = "discord_webhook"; + public const string NotificationHttpClient = "watchdog_notification"; private readonly HttpClient _httpClient = http.CreateClient(); @@ -26,13 +28,26 @@ public void SendNotification(string message) var optionsValue = options.Value; if (optionsValue.DiscordWebhook == null) { - logger.LogTrace("Not sending notification: no Discord webhook URL configured"); + logger.LogTrace("Not sending discord notification: no Discord webhook URL configured"); return; } SendWebhook(optionsValue, message); } + public async Task SendHttpNotification(string instanceId) + { + var optionsValue = options.Value; + + if (optionsValue.NotifyUrls.Count == 0) + { + logger.LogTrace("Not sending HTTP notification: no HTTP URLs configured"); + return; + } + + await SendUpdatePosts(optionsValue, instanceId); + } + private async void SendWebhook(NotificationOptions optionsValue, string message) { logger.LogTrace("Sending notification \"{Message}\" to Discord webhook...", message); @@ -60,6 +75,46 @@ private async void SendWebhook(NotificationOptions optionsValue, string message) } } + private async Task SendUpdatePosts(NotificationOptions optionsValue, string instanceId) + { + logger.LogTrace("Sending update notification post..."); + + try + { + var data = new UpdatePostExecute + { + InstanceId = instanceId + }; + + foreach (var urlString in optionsValue.NotifyUrls) + { + await SendSpecificUpdatePost(optionsValue, data, new Uri(urlString)); + } + } + catch (Exception e) + { + logger.LogError(e, "Error while sending update notification post!"); + } + } + + private async Task SendSpecificUpdatePost(NotificationOptions optionsValue, UpdatePostExecute data, Uri url) + { + using var request = new HttpRequestMessage(HttpMethod.Post, url); + + if (!string.IsNullOrWhiteSpace(optionsValue.UpdatePostToken)) + { + var authHeader = new AuthenticationHeaderValue("WatchdogToken", optionsValue.UpdatePostToken); + request.Headers.Authorization = authHeader; + } + + request.Content = JsonContent.Create(data); + + using var response = await _httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + + logger.LogTrace("Succeeded sending update post"); + } + private sealed class DiscordWebhookExecute { public string? Content { get; set; } @@ -76,4 +131,12 @@ private sealed class DiscordAllowedMentions [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.KebabCaseLower)] [JsonSerializable(typeof(DiscordWebhookExecute))] private partial class DiscordSourceGenerationContext : JsonSerializerContext; + + private sealed class UpdatePostExecute + { + /// + /// The Watchdog instance that was updated. + /// + public required string InstanceId { get; set; } + } } diff --git a/SS14.Watchdog/Components/Notifications/NotificationOptions.cs b/SS14.Watchdog/Components/Notifications/NotificationOptions.cs index ed7b36f..d11318d 100644 --- a/SS14.Watchdog/Components/Notifications/NotificationOptions.cs +++ b/SS14.Watchdog/Components/Notifications/NotificationOptions.cs @@ -1,4 +1,6 @@ -namespace SS14.Watchdog.Components.Notifications; +using System.Collections.Generic; + +namespace SS14.Watchdog.Components.Notifications; /// /// Options for notifications the watchdog can send via various channels. @@ -12,4 +14,16 @@ public sealed class NotificationOptions /// A Discord webhook URL to send notifications like server crashes to. /// public string? DiscordWebhook { get; set; } + + /// + /// A list of URLs that should be sent a POST request. + /// If specified, each one will be provided as authentication. + /// + /// No suffixes or prefixes are added to the URL before sending the POST. + public List NotifyUrls { get; set; } = []; + + /// + /// The token that will be passed through to each update URL as BasicAuthentication. + /// + public string UpdatePostToken { get; set; } = string.Empty; } diff --git a/SS14.Watchdog/Components/ServerManagement/ServerInstance.Actor.cs b/SS14.Watchdog/Components/ServerManagement/ServerInstance.Actor.cs index 5237116..cda1d16 100644 --- a/SS14.Watchdog/Components/ServerManagement/ServerInstance.Actor.cs +++ b/SS14.Watchdog/Components/ServerManagement/ServerInstance.Actor.cs @@ -191,6 +191,9 @@ private async Task RunCommandUpdateAvailable(CommandUpdateAvailable command, Can _updateOnRestart = command.UpdateAvailable; if (command.UpdateAvailable) { + // Should send an update regardless of what the server's state is. + await _notificationManager.SendHttpNotification(Key); + if (IsRunning) { _logger.LogTrace("Server is running, sending update notification."); diff --git a/SS14.Watchdog/Startup.cs b/SS14.Watchdog/Startup.cs index 34375ad..5976367 100644 --- a/SS14.Watchdog/Startup.cs +++ b/SS14.Watchdog/Startup.cs @@ -68,7 +68,7 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(); - services.AddHttpClient(NotificationManager.DiscordHttpClient); + services.AddHttpClient(NotificationManager.NotificationHttpClient); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. From 3fb5d3b6e8d89c6cab697d7f90dc44ba73607e0d Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sat, 22 Nov 2025 20:24:46 -0400 Subject: [PATCH 2/6] use basic --- .../Components/Notifications/NotificationManager.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/SS14.Watchdog/Components/Notifications/NotificationManager.cs b/SS14.Watchdog/Components/Notifications/NotificationManager.cs index 03a6d82..d04811a 100644 --- a/SS14.Watchdog/Components/Notifications/NotificationManager.cs +++ b/SS14.Watchdog/Components/Notifications/NotificationManager.cs @@ -2,6 +2,7 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Net.Http.Json; +using System.Text; using System.Text.Json.Serialization; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -103,7 +104,9 @@ private async Task SendSpecificUpdatePost(NotificationOptions optionsValue, Upda if (!string.IsNullOrWhiteSpace(optionsValue.UpdatePostToken)) { - var authHeader = new AuthenticationHeaderValue("WatchdogToken", optionsValue.UpdatePostToken); + var tokenAsBytes = Encoding.ASCII.GetBytes(optionsValue.UpdatePostToken); + var tokenAsBase64 = Convert.ToBase64String(tokenAsBytes); + var authHeader = new AuthenticationHeaderValue("Basic", tokenAsBase64); request.Headers.Authorization = authHeader; } From 4eb3a949edf0dc2da65b1742371de8914a9ad3eb Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 23 Nov 2025 21:25:48 -0400 Subject: [PATCH 3/6] silly --- .../Components/Notifications/NotificationManager.cs | 6 +++--- .../Components/Notifications/NotificationOptions.cs | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/SS14.Watchdog/Components/Notifications/NotificationManager.cs b/SS14.Watchdog/Components/Notifications/NotificationManager.cs index d04811a..a131970 100644 --- a/SS14.Watchdog/Components/Notifications/NotificationManager.cs +++ b/SS14.Watchdog/Components/Notifications/NotificationManager.cs @@ -104,9 +104,9 @@ private async Task SendSpecificUpdatePost(NotificationOptions optionsValue, Upda if (!string.IsNullOrWhiteSpace(optionsValue.UpdatePostToken)) { - var tokenAsBytes = Encoding.ASCII.GetBytes(optionsValue.UpdatePostToken); - var tokenAsBase64 = Convert.ToBase64String(tokenAsBytes); - var authHeader = new AuthenticationHeaderValue("Basic", tokenAsBase64); + var authAsBytes = Encoding.ASCII.GetBytes(optionsValue.UpdatePostUser + ":" + optionsValue.UpdatePostToken); + var authAsBase64 = Convert.ToBase64String(authAsBytes); + var authHeader = new AuthenticationHeaderValue("Basic", authAsBase64); request.Headers.Authorization = authHeader; } diff --git a/SS14.Watchdog/Components/Notifications/NotificationOptions.cs b/SS14.Watchdog/Components/Notifications/NotificationOptions.cs index d11318d..063d407 100644 --- a/SS14.Watchdog/Components/Notifications/NotificationOptions.cs +++ b/SS14.Watchdog/Components/Notifications/NotificationOptions.cs @@ -22,6 +22,11 @@ public sealed class NotificationOptions /// No suffixes or prefixes are added to the URL before sending the POST. public List NotifyUrls { get; set; } = []; + /// + /// The username that will be passed through to each update URL as BasicAuthentication. + /// + public string UpdatePostUser { get; set; } = string.Empty; + /// /// The token that will be passed through to each update URL as BasicAuthentication. /// From 1dd0dcd8b1125838e4b5074e5444556be70928f9 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 23 Nov 2025 21:26:20 -0400 Subject: [PATCH 4/6] double silly --- SS14.Watchdog/Components/Notifications/NotificationManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SS14.Watchdog/Components/Notifications/NotificationManager.cs b/SS14.Watchdog/Components/Notifications/NotificationManager.cs index a131970..d1e9560 100644 --- a/SS14.Watchdog/Components/Notifications/NotificationManager.cs +++ b/SS14.Watchdog/Components/Notifications/NotificationManager.cs @@ -102,7 +102,8 @@ private async Task SendSpecificUpdatePost(NotificationOptions optionsValue, Upda { using var request = new HttpRequestMessage(HttpMethod.Post, url); - if (!string.IsNullOrWhiteSpace(optionsValue.UpdatePostToken)) + if (!string.IsNullOrWhiteSpace(optionsValue.UpdatePostUser) + && !string.IsNullOrWhiteSpace(optionsValue.UpdatePostToken)) { var authAsBytes = Encoding.ASCII.GetBytes(optionsValue.UpdatePostUser + ":" + optionsValue.UpdatePostToken); var authAsBase64 = Convert.ToBase64String(authAsBytes); From 00a083469973527e4856cf438d4865970fa5f292 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Mon, 24 Nov 2025 07:14:38 -0400 Subject: [PATCH 5/6] testing --- SS14.Watchdog/Components/Notifications/NotificationManager.cs | 2 +- .../Components/ServerManagement/ServerInstance.Actor.cs | 3 --- SS14.Watchdog/Components/ServerManagement/ServerInstance.cs | 4 ++++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/SS14.Watchdog/Components/Notifications/NotificationManager.cs b/SS14.Watchdog/Components/Notifications/NotificationManager.cs index d1e9560..2048650 100644 --- a/SS14.Watchdog/Components/Notifications/NotificationManager.cs +++ b/SS14.Watchdog/Components/Notifications/NotificationManager.cs @@ -36,7 +36,7 @@ public void SendNotification(string message) SendWebhook(optionsValue, message); } - public async Task SendHttpNotification(string instanceId) + public async void SendHttpNotification(string instanceId) { var optionsValue = options.Value; diff --git a/SS14.Watchdog/Components/ServerManagement/ServerInstance.Actor.cs b/SS14.Watchdog/Components/ServerManagement/ServerInstance.Actor.cs index cda1d16..5237116 100644 --- a/SS14.Watchdog/Components/ServerManagement/ServerInstance.Actor.cs +++ b/SS14.Watchdog/Components/ServerManagement/ServerInstance.Actor.cs @@ -191,9 +191,6 @@ private async Task RunCommandUpdateAvailable(CommandUpdateAvailable command, Can _updateOnRestart = command.UpdateAvailable; if (command.UpdateAvailable) { - // Should send an update regardless of what the server's state is. - await _notificationManager.SendHttpNotification(Key); - if (IsRunning) { _logger.LogTrace("Server is running, sending update notification."); diff --git a/SS14.Watchdog/Components/ServerManagement/ServerInstance.cs b/SS14.Watchdog/Components/ServerManagement/ServerInstance.cs index e166c80..26c7e1d 100644 --- a/SS14.Watchdog/Components/ServerManagement/ServerInstance.cs +++ b/SS14.Watchdog/Components/ServerManagement/ServerInstance.cs @@ -336,6 +336,10 @@ public void HandleUpdateCheck() return; _logger.LogDebug("Received update notification."); + + // Should send an update regardless of what the server's state is. + _notificationManager.SendHttpNotification(Key); + _taskQueue.QueueTask(async cancel => { var updateAvailable = await _updateProvider.CheckForUpdateAsync(_currentRevision, cancel); From c8c96482bf6a312c38a00af8d860d01c05b83a61 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Mon, 24 Nov 2025 07:21:46 -0400 Subject: [PATCH 6/6] okay fixed --- SS14.Watchdog/Components/Notifications/NotificationManager.cs | 2 +- .../Components/ServerManagement/ServerInstance.Actor.cs | 3 +++ SS14.Watchdog/Components/ServerManagement/ServerInstance.cs | 3 --- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SS14.Watchdog/Components/Notifications/NotificationManager.cs b/SS14.Watchdog/Components/Notifications/NotificationManager.cs index 2048650..d1e9560 100644 --- a/SS14.Watchdog/Components/Notifications/NotificationManager.cs +++ b/SS14.Watchdog/Components/Notifications/NotificationManager.cs @@ -36,7 +36,7 @@ public void SendNotification(string message) SendWebhook(optionsValue, message); } - public async void SendHttpNotification(string instanceId) + public async Task SendHttpNotification(string instanceId) { var optionsValue = options.Value; diff --git a/SS14.Watchdog/Components/ServerManagement/ServerInstance.Actor.cs b/SS14.Watchdog/Components/ServerManagement/ServerInstance.Actor.cs index 5237116..cda1d16 100644 --- a/SS14.Watchdog/Components/ServerManagement/ServerInstance.Actor.cs +++ b/SS14.Watchdog/Components/ServerManagement/ServerInstance.Actor.cs @@ -191,6 +191,9 @@ private async Task RunCommandUpdateAvailable(CommandUpdateAvailable command, Can _updateOnRestart = command.UpdateAvailable; if (command.UpdateAvailable) { + // Should send an update regardless of what the server's state is. + await _notificationManager.SendHttpNotification(Key); + if (IsRunning) { _logger.LogTrace("Server is running, sending update notification."); diff --git a/SS14.Watchdog/Components/ServerManagement/ServerInstance.cs b/SS14.Watchdog/Components/ServerManagement/ServerInstance.cs index 26c7e1d..c09b3a9 100644 --- a/SS14.Watchdog/Components/ServerManagement/ServerInstance.cs +++ b/SS14.Watchdog/Components/ServerManagement/ServerInstance.cs @@ -337,9 +337,6 @@ public void HandleUpdateCheck() _logger.LogDebug("Received update notification."); - // Should send an update regardless of what the server's state is. - _notificationManager.SendHttpNotification(Key); - _taskQueue.QueueTask(async cancel => { var updateAvailable = await _updateProvider.CheckForUpdateAsync(_currentRevision, cancel);