Paralleles WebGUI mit Kestrel, Token-Sicherung und SSE-Logs implementiert
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using PolyTraderSharp.Services;
|
||||
|
||||
namespace PolyTrader.Web.Endpoints
|
||||
{
|
||||
public static class LogsEndpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapLogsEndpoints(this IEndpointRouteBuilder routes)
|
||||
{
|
||||
var group = routes.MapGroup("/api/logs");
|
||||
|
||||
group.MapGet("/", (TerminalLogger logger, int? minutes) =>
|
||||
{
|
||||
var span = TimeSpan.FromMinutes(minutes.GetValueOrDefault(60));
|
||||
var history = logger.GetHistory(span)
|
||||
.Select(e => new
|
||||
{
|
||||
time = e.Timestamp.ToString("HH:mm:ss"),
|
||||
timestamp = e.Timestamp,
|
||||
level = e.Level.ToString(),
|
||||
cid = e.CorrelationId,
|
||||
msg = e.Message
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return Results.Ok(history);
|
||||
});
|
||||
|
||||
group.MapGet("/stream", async (HttpContext context, TerminalLogger logger, CancellationToken ct) =>
|
||||
{
|
||||
context.Response.Headers.ContentType = "text/event-stream";
|
||||
context.Response.Headers.CacheControl = "no-cache";
|
||||
context.Response.Headers.Connection = "keep-alive";
|
||||
|
||||
var channel = Channel.CreateUnbounded<LogMessageEventArgs>(new UnboundedChannelOptions
|
||||
{
|
||||
SingleWriter = false,
|
||||
SingleReader = true
|
||||
});
|
||||
|
||||
void Handler(object? _, LogMessageEventArgs e)
|
||||
{
|
||||
channel.Writer.TryWrite(e);
|
||||
}
|
||||
|
||||
logger.OnLogMessage += Handler;
|
||||
|
||||
try
|
||||
{
|
||||
await context.Response.WriteAsync(": connected\n\n", ct);
|
||||
await context.Response.Body.FlushAsync(ct);
|
||||
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
var log = await channel.Reader.ReadAsync(ct);
|
||||
var payload = JsonSerializer.Serialize(new
|
||||
{
|
||||
time = log.Timestamp.ToString("HH:mm:ss"),
|
||||
level = log.Level.ToString(),
|
||||
cid = log.CorrelationId,
|
||||
msg = log.Message
|
||||
});
|
||||
|
||||
await context.Response.WriteAsync($"data: {payload}\n\n", ct);
|
||||
await context.Response.Body.FlushAsync(ct);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Client hat Verbindung getrennt
|
||||
}
|
||||
finally
|
||||
{
|
||||
logger.OnLogMessage -= Handler;
|
||||
channel.Writer.TryComplete();
|
||||
}
|
||||
});
|
||||
|
||||
return routes;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user