114 lines
4.2 KiB
C#
114 lines
4.2 KiB
C#
using System;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using FinlyticCore.Dtos;
|
|
using FinlyticCore.Services;
|
|
using FinlyticCore.Util;
|
|
using FinlyticNotify.Settings;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace FinlyticNotify.Services;
|
|
|
|
/// <summary>
|
|
/// Service interface for resolving a UserId GUID to a clean username for ntfy channel addressing.
|
|
/// </summary>
|
|
public interface IUserTradeResolver
|
|
{
|
|
/// <summary>
|
|
/// Resolves the clean username for a given UserId GUID via in-memory cache and FinlyticBackend MQTT RPC.
|
|
/// </summary>
|
|
/// <param name="userId">The unique ID of the user owning the trade.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>The resolved username (e.g. "lars", "kleidukos", "admin") for ntfy channel addressing.</returns>
|
|
Task<string> ResolveUsernameByUserIdAsync(Guid userId, CancellationToken cancellationToken = default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Implementation of <see cref="IUserTradeResolver"/> performing cached MQTT RPC calls to FinlyticBackend
|
|
/// without any direct cross-database dependencies.
|
|
/// </summary>
|
|
public class UserTradeResolver : IUserTradeResolver
|
|
{
|
|
private readonly IServiceProvider _serviceProvider;
|
|
private readonly IMemoryCache _cache;
|
|
private readonly ISettingsService _settingsService;
|
|
private readonly IConfiguration _configuration;
|
|
private readonly ILogger<UserTradeResolver> _logger;
|
|
|
|
private static readonly Regex InvalidChannelCharRegex = new("[^a-zA-Z0-9_-]", RegexOptions.Compiled);
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="UserTradeResolver"/> class.
|
|
/// </summary>
|
|
public UserTradeResolver(
|
|
IServiceProvider serviceProvider,
|
|
IMemoryCache cache,
|
|
ISettingsService settingsService,
|
|
IConfiguration configuration,
|
|
ILogger<UserTradeResolver> logger)
|
|
{
|
|
_serviceProvider = serviceProvider;
|
|
_cache = cache;
|
|
_settingsService = settingsService;
|
|
_configuration = configuration;
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<string> ResolveUsernameByUserIdAsync(Guid userId, CancellationToken cancellationToken = default)
|
|
{
|
|
string defaultUsername = await _settingsService.GetSettingAsync(NotifySettingKeys.NtfyDefaultUsername, cancellationToken);
|
|
if (string.IsNullOrWhiteSpace(defaultUsername))
|
|
{
|
|
defaultUsername = _configuration.GetValue<string>("Ntfy:DefaultUsername") ?? "admin";
|
|
}
|
|
|
|
if (userId == Guid.Empty)
|
|
{
|
|
return defaultUsername;
|
|
}
|
|
|
|
string cacheKey = $"user_name_{userId}";
|
|
if (_cache.TryGetValue(cacheKey, out string? cachedUsername) && !string.IsNullOrWhiteSpace(cachedUsername))
|
|
{
|
|
return cachedUsername;
|
|
}
|
|
|
|
try
|
|
{
|
|
var mqttClient = _serviceProvider.GetService<NotifyMqttClient>();
|
|
if (mqttClient != null && mqttClient.IsConnected)
|
|
{
|
|
var username = await mqttClient.SendRpcRequestAsync<string, UserIdRequest>(
|
|
MqttTopics.Channels.BackendGetUsername,
|
|
new UserIdRequest(userId),
|
|
TimeSpan.FromSeconds(2));
|
|
|
|
if (!string.IsNullOrWhiteSpace(username))
|
|
{
|
|
string cleanChannel = CleanUsername(username);
|
|
_cache.Set(cacheKey, cleanChannel, TimeSpan.FromHours(1));
|
|
return cleanChannel;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning(ex, "[UserTradeResolver] Failed to resolve username from FinlyticBackend for UserId {UserId}. Falling back to default.", userId);
|
|
}
|
|
|
|
return defaultUsername;
|
|
}
|
|
|
|
private static string CleanUsername(string rawName)
|
|
{
|
|
var cleaned = rawName.Trim().ToLowerInvariant().Replace(" ", "_");
|
|
cleaned = InvalidChannelCharRegex.Replace(cleaned, "");
|
|
return string.IsNullOrWhiteSpace(cleaned) ? "admin" : cleaned;
|
|
}
|
|
}
|