feat(backend): add admin evaluation history, engine, simulation, bot API controllers and global exception middleware

This commit is contained in:
2026-08-24 21:37:32 +02:00
parent 6974b2075b
commit 676496b77d
37 changed files with 88203 additions and 83075 deletions
+21 -68
View File
@@ -1,91 +1,44 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using FinlyticCore.Models;
using FinlyticCore.Util;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace FinlyticBackend.Util;
/// <summary>
/// Managed MQTT client for web gateway endpoints enabling RPC communication with background microservices.
/// Backward-compatible bridge forwarding RPC requests to the primary singleton <see cref="BackendMqttBridge"/>.
/// Prevents secondary MQTT connection instantiations in the gateway process.
/// </summary>
public class WebMqttClient : ManagedMqttClient, IHostedService
public class WebMqttClient : IHostedService
{
private readonly ILogger<WebMqttClient> _logger;
private readonly IConfiguration _configuration;
private readonly BackendMqttBridge _bridge;
public WebMqttClient(ILogger<WebMqttClient> logger, IConfiguration configuration) : base(logger)
public bool IsConnected => _bridge.IsConnected;
public WebMqttClient(BackendMqttBridge bridge)
{
_logger = logger;
_configuration = configuration;
_bridge = bridge;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
var config = new MqttConfiguration
{
Host = _configuration["MQTT:Host"] ?? _configuration["MQTT__Host"] ?? "localhost",
Port = Convert.ToInt32(_configuration["MQTT:Port"] ?? _configuration["MQTT__Port"] ?? "1883"),
ClientId = $"{(_configuration["MQTT:ClientId"] ?? _configuration["MQTT__ClientId"] ?? "finlytic_backend_rpc")}_{Guid.NewGuid()}"
};
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
_logger.LogInformation("Starting Web MQTT RPC Client. Host: {Host}, ClientId: {ClientId}", config.Host, config.ClientId);
await ConnectAsync(config);
public Task<TResponse?> SendRpcRequestAsync<TResponse, TRequest>(string channel, TRequest request, TimeSpan timeout)
where TResponse : class
where TRequest : class
{
return _bridge.SendRpcRequestAsync<TResponse, TRequest>(channel, request, timeout);
}
public async Task StopAsync(CancellationToken cancellationToken)
public Task<TResponse?> SendRpcRequestAsync<TResponse, TRequest>(string channel, TRequest request)
where TResponse : class
where TRequest : class
{
_logger.LogInformation("Stopping Web MQTT RPC Client.");
await DisconnectAsync();
return _bridge.SendRpcRequestAsync<TResponse, TRequest>(channel, request);
}
protected override async Task OnConnectedAsync()
public Task PublishAsync<T>(string topic, T payload)
{
_logger.LogInformation("Web MQTT RPC client connected. Subscribing to RPC response channels...");
await SubscribeAsync("services/response/news_Get/#");
await SubscribeAsync("services/response/news_GetDaily/#");
await SubscribeAsync("services/response/news_GetById/#");
await SubscribeAsync("services/response/sentiment_GetArticle/#");
await SubscribeAsync("services/response/sentiment_GetIsin/#");
await SubscribeAsync("services/response/sentiment_Analyze/#");
await SubscribeAsync("services/response/fundamentals_Get/#");
await SubscribeAsync("services/response/events_GetAll/#");
await SubscribeAsync("services/response/events_GetByMonth/#");
await SubscribeAsync("services/response/ta_GetAnalysis/#");
await SubscribeAsync("services/response/tr_GetLivePrice/#");
await SubscribeAsync("services/response/assets_Get/#");
await SubscribeAsync("services/response/assets_Search/#");
await SubscribeAsync("services/response/assets_GetDiscovery/#");
await SubscribeAsync("services/response/assets_GetDerivatives/#");
await SubscribeAsync("services/response/trades_Get/#");
await SubscribeAsync("services/response/trades_Close/#");
await SubscribeAsync("services/response/trades_Reject/#");
await SubscribeAsync("services/response/trades_Accept/#");
await SubscribeAsync("services/response/analyzer_TriggerManual/#");
await SubscribeAsync("services/response/health_Ping/#");
// Settings RPC response channels for all microservices
await SubscribeAsync("services/response/fundamentals_settings_GetAll/#");
await SubscribeAsync("services/response/fundamentals_settings_Update/#");
await SubscribeAsync("services/response/news_settings_GetAll/#");
await SubscribeAsync("services/response/news_settings_Update/#");
await SubscribeAsync("services/response/ta_settings_GetAll/#");
await SubscribeAsync("services/response/ta_settings_Update/#");
await SubscribeAsync("services/response/sentiment_settings_GetAll/#");
await SubscribeAsync("services/response/sentiment_settings_Update/#");
await SubscribeAsync("services/response/analyzer_settings_GetAll/#");
await SubscribeAsync("services/response/analyzer_settings_Update/#");
await SubscribeAsync("services/response/trades_settings_GetAll/#");
await SubscribeAsync("services/response/trades_settings_Update/#");
await SubscribeAsync("services/response/assets_settings_GetAll/#");
await SubscribeAsync("services/response/assets_settings_Update/#");
}
protected override Task OnMessageReceivedAsync(string topic, string payload)
{
return Task.CompletedTask;
return _bridge.PublishAsync(topic, payload);
}
}