Files
webhook-server/src/WebhookServer.Gui/Services/AdminPipeClient.cs
T
justin b17d832842
Sync Wiki / sync (push) Has been cancelled
CI / build (push) Has been cancelled
Sync from GitHub main: v0.1.1 + v0.1.2 + wiki sync (#3)
2026-05-08 11:14:17 -04:00

107 lines
5.0 KiB
C#

using System.IO;
using System.IO.Pipes;
using System.Runtime.Versioning;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using WebhookServer.Core.Ipc;
using WebhookServer.Core.Models;
namespace WebhookServer.Gui.Services;
/// <summary>
/// Thin client around the admin named pipe. Each call connects, sends one request,
/// reads one response, and disconnects — keeps lifecycle simple at the cost of
/// connect-per-call overhead. The service single-instance pipe queues requests so
/// concurrent calls from the GUI serialize automatically.
/// </summary>
[SupportedOSPlatform("windows")]
public sealed class AdminPipeClient
{
public TimeSpan ConnectTimeout { get; init; } = TimeSpan.FromSeconds(5);
public async Task<AdminResponse> InvokeAsync(string op, object? data = null, CancellationToken ct = default)
{
var request = new AdminRequest
{
Op = op,
Data = data is null
? null
: JsonSerializer.SerializeToDocument(data, AdminProtocol.JsonOptions).RootElement.Clone(),
};
await using var pipe = new NamedPipeClientStream(
".",
PipeSecurityFactory.PipeName,
PipeDirection.InOut,
PipeOptions.Asynchronous);
await pipe.ConnectAsync((int)ConnectTimeout.TotalMilliseconds, ct).ConfigureAwait(false);
await PipeFraming.WriteAsync(pipe, request, ct).ConfigureAwait(false);
using var reader = PipeFraming.CreateReader(pipe);
var response = await PipeFraming.ReadAsync<AdminResponse>(reader, ct).ConfigureAwait(false);
return response ?? AdminResponse.Failure("empty response from service");
}
public async Task<T?> InvokeAsync<T>(string op, object? data = null, CancellationToken ct = default) where T : class
{
var resp = await InvokeAsync(op, data, ct).ConfigureAwait(false);
if (!resp.Ok || resp.Data is null) return null;
return resp.Data.Value.Deserialize<T>(AdminProtocol.JsonOptions);
}
public Task<AdminResponse> PingAsync(CancellationToken ct = default) =>
InvokeAsync(AdminOps.Ping, null, ct);
public Task<StatusInfo?> GetStatusAsync(CancellationToken ct = default) =>
InvokeAsync<StatusInfo>(AdminOps.GetStatus, null, ct);
public Task<ServerConfig?> GetConfigAsync(CancellationToken ct = default) =>
InvokeAsync<ServerConfig>(AdminOps.GetConfig, null, ct);
public Task<EndpointConfig?> CreateEndpointAsync(EndpointConfig endpoint, CancellationToken ct = default) =>
InvokeAsync<EndpointConfig>(AdminOps.CreateEndpoint, endpoint, ct);
public Task<EndpointConfig?> UpdateEndpointAsync(EndpointConfig endpoint, CancellationToken ct = default) =>
InvokeAsync<EndpointConfig>(AdminOps.UpdateEndpoint, endpoint, ct);
public Task<AdminResponse> DeleteEndpointAsync(Guid id, CancellationToken ct = default) =>
InvokeAsync(AdminOps.DeleteEndpoint, new DeleteEndpointArgs { Id = id }, ct);
public Task<AdminResponse> SetEndpointEnabledAsync(Guid id, bool enabled, CancellationToken ct = default) =>
InvokeAsync(enabled ? AdminOps.EnableEndpoint : AdminOps.DisableEndpoint, new EndpointToggle { Id = id }, ct);
public Task<AdminResponse> BindHttpsAsync(HttpsBinding? binding, CancellationToken ct = default) =>
InvokeAsync(AdminOps.BindHttps, binding, ct);
public Task<AdminResponse> RestartListenerAsync(CancellationToken ct = default) =>
InvokeAsync(AdminOps.RestartListener, null, ct);
public async Task<List<LogLine>> TailLogsAsync(int lines, CancellationToken ct = default)
{
var resp = await InvokeAsync(AdminOps.TailLogs, new TailLogsArgs { LinesToBacklog = lines, Follow = false }, ct).ConfigureAwait(false);
if (!resp.Ok || resp.Data is null) return new List<LogLine>();
var lst = resp.Data.Value.GetProperty("lines").Deserialize<List<LogLine>>(AdminProtocol.JsonOptions);
return lst ?? new List<LogLine>();
}
public async Task<List<BackupEntry>> ListBackupsAsync(CancellationToken ct = default)
{
var resp = await InvokeAsync(AdminOps.ListBackups, null, ct).ConfigureAwait(false);
if (!resp.Ok || resp.Data is null) return new List<BackupEntry>();
var lst = resp.Data.Value.GetProperty("backups").Deserialize<List<BackupEntry>>(AdminProtocol.JsonOptions);
return lst ?? new List<BackupEntry>();
}
public Task<AdminResponse> RestoreBackupAsync(string fileName, CancellationToken ct = default) =>
InvokeAsync(AdminOps.RestoreBackup, new RestoreBackupArgs { FileName = fileName }, ct);
public Task<AdminResponse> ImportConfigAsync(ServerConfig config, CancellationToken ct = default) =>
InvokeAsync(AdminOps.ImportConfig, config, ct);
public Task<BackupEntry?> CreateCheckpointAsync(string? description, CancellationToken ct = default) =>
InvokeAsync<BackupEntry>(AdminOps.CreateCheckpoint, new CreateCheckpointArgs { Description = description }, ct);
}