Add resilient upload queue and client health
Some checks failed
OfficeCom Sentinel Client / validate-client (push) Successful in 24s
OfficeCom Sentinel Client / build-client-windows (push) Failing after 18s

This commit is contained in:
OfficeCom Codex
2026-07-26 20:25:25 +02:00
parent dfdae7a532
commit e7fd4e7ef5
11 changed files with 282 additions and 25 deletions

View File

@@ -1,4 +1,5 @@
using OCSentinelCli.Configuration;
using OCSentinelCli.Models;
using System.Text.Json;
using OCSentinelCli.Security;
using OCSentinelCli.Transport;
@@ -56,37 +57,103 @@ internal static class UploadCommand
string reportFullPath = Path.GetFullPath(reportPath);
string json = File.ReadAllText(reportFullPath);
string secret = ProtectedSecretStore.LoadSecret(resolvedSecretPath);
var client = new N8nUploadClient();
var queue = new UploadQueue(config.UploadQueueMaxReports);
UploadHealth health = queue.LoadHealth();
DateTimeOffset attemptTime = DateTimeOffset.UtcNow;
ScanResult? parsedReport = JsonSerializer.Deserialize<ScanResult>(json, JsonOptions.Default);
if (parsedReport is not null)
{
parsedReport = parsedReport with
{
Runtime = parsedReport.Runtime with
{
UploadAttempted = true
UploadAttempted = true,
UploadStatus = "attempting",
QueuedReportCount = health.QueuedReportCount,
LastSuccessfulUploadUtc = health.LastSuccessfulUploadUtc
}
};
json = JsonSerializer.Serialize(parsedReport, JsonOptions.Default);
File.WriteAllText(reportFullPath, json);
}
string secret = ProtectedSecretStore.LoadSecret(resolvedSecretPath);
var client = new N8nUploadClient();
var result = client.UploadJson(config.N8nWebhookUrl, Environment.MachineName, BuildMetadata.Version, json, secret, config.UploadTimeoutSeconds);
UploadResult? deferredFailure = queue.Drain(
client,
config.N8nWebhookUrl,
Environment.MachineName,
BuildMetadata.Version,
secret,
config.UploadTimeoutSeconds);
if (!result.Success)
if (deferredFailure is null)
{
Console.Error.WriteLine($"Upload failed ({result.StatusCode}): {result.Message}");
return 1;
var result = client.UploadJson(config.N8nWebhookUrl, Environment.MachineName, BuildMetadata.Version, json, secret, config.UploadTimeoutSeconds);
if (result.Success)
{
UploadHealth successHealth = new()
{
LastUploadAttemptUtc = attemptTime,
LastSuccessfulUploadUtc = DateTimeOffset.UtcNow,
LastUploadStatus = "ok",
LastUploadError = string.Empty,
QueuedReportCount = queue.GetQueueDepth()
};
queue.SaveHealth(successHealth);
WriteReportRuntime(reportFullPath, parsedReport, successHealth, true);
Console.WriteLine($"Upload succeeded ({result.StatusCode})");
Console.WriteLine($"Nonce: {result.Nonce}");
Console.WriteLine($"Payload SHA256: {result.PayloadSha256}");
return 0;
}
deferredFailure = result;
}
Console.WriteLine($"Upload succeeded ({result.StatusCode})");
Console.WriteLine($"Nonce: {result.Nonce}");
Console.WriteLine($"Payload SHA256: {result.PayloadSha256}");
UploadResult failure = deferredFailure ?? throw new InvalidOperationException("Upload failed without a result.");
int queuedCount = queue.Enqueue(json);
UploadHealth queuedHealth = new()
{
LastUploadAttemptUtc = attemptTime,
LastSuccessfulUploadUtc = health.LastSuccessfulUploadUtc,
LastUploadStatus = "queued",
LastUploadError = failure.Message,
QueuedReportCount = queuedCount
};
queue.SaveHealth(queuedHealth);
WriteReportRuntime(reportFullPath, parsedReport, queuedHealth, true);
Console.WriteLine($"Upload deferred ({failure.StatusCode}): {failure.Message}");
Console.WriteLine($"Queued reports: {queuedCount}");
Console.WriteLine("The report will be retried automatically on the next scheduled run.");
return 0;
}
private static void WriteReportRuntime(string reportPath, ScanResult? report, UploadHealth health, bool attempted)
{
if (report is null)
{
return;
}
ScanResult updated = report with
{
Runtime = report.Runtime with
{
UploadAttempted = attempted,
UploadSucceeded = string.Equals(health.LastUploadStatus, "ok", StringComparison.Ordinal),
UploadStatus = health.LastUploadStatus,
QueuedReportCount = health.QueuedReportCount,
LastSuccessfulUploadUtc = health.LastSuccessfulUploadUtc,
LastUploadError = health.LastUploadError
}
};
File.WriteAllText(reportPath, JsonSerializer.Serialize(updated, JsonOptions.Default));
}
private static string ReadValue(string[] args, ref int index, string argName)
{
if (index + 1 >= args.Length)