feat(assets): dynamic settings, IFinlyticLogger, live log streaming, and EF migration

This commit is contained in:
2026-08-15 21:30:46 +02:00
parent 57554a9582
commit 1f9d66405a
11 changed files with 855 additions and 384 deletions
+18 -20
View File
@@ -1,7 +1,13 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using FinlyticAssets.Models;
using FinlyticAssets.Util;
using FinlyticCore.Services;
namespace FinlyticAssets.Services;
@@ -13,8 +19,6 @@ public interface IAssetsIndexService
/// <summary>
/// Recreates the index file containing basic asset identifiers (ISIN and Name) for all active, valid assets.
/// </summary>
/// <param name="cancellationToken">A token to monitor for cancellation requests.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public Task ReCreateIndexFileAsync(CancellationToken cancellationToken = default);
/// <summary>
@@ -28,18 +32,13 @@ public interface IAssetsIndexService
/// </summary>
public class AssetsIndexService : IAssetsIndexService
{
private readonly ILogger<AssetsIndexService> _logger;
private readonly IFinlyticLogger<AssetsIndexService> _finlyticLogger;
private readonly IAssetsDbService _assetsDbService;
private static readonly HttpClient _httpClient = new();
/// <summary>
/// Initializes a new instance of the <see cref="AssetsIndexService"/> class.
/// </summary>
/// <param name="logger">The logger for documenting indexing events and errors.</param>
/// <param name="assetsDbService">The database service to query the assets from.</param>
public AssetsIndexService(ILogger<AssetsIndexService> logger, IAssetsDbService assetsDbService)
public AssetsIndexService(IFinlyticLogger<AssetsIndexService> finlyticLogger, IAssetsDbService assetsDbService)
{
_logger = logger;
_finlyticLogger = finlyticLogger;
_assetsDbService = assetsDbService;
}
@@ -51,7 +50,7 @@ public class AssetsIndexService : IAssetsIndexService
var assets = await _assetsDbService.GetAllValidAssetsAsync();
if (assets == null || !assets.Any())
{
_logger.LogWarning("[{Channel}] No valid assets found in the database to index.", "AssetsChannel");
await _finlyticLogger.LogWarningAsync(SettingKeys.AssetsChannel, "[AssetsIndexService] No valid assets found in the database to index.");
return;
}
@@ -59,7 +58,6 @@ public class AssetsIndexService : IAssetsIndexService
.DistinctBy(a => a.Isin)
.Select(a => {
string cleanIsin = a.Isin.Trim().ToUpperInvariant();
// Point directly to our own local backend logo endpoint
string imageUrl = $"/api/v1/logo/{cleanIsin}";
return new AssetIndex(cleanIsin, a.Name, imageUrl);
}).ToList();
@@ -78,22 +76,22 @@ public class AssetsIndexService : IAssetsIndexService
await JsonSerializer.SerializeAsync(fileStream, indexAssets, cancellationToken: cancellationToken);
}
_logger.LogInformation("[{Channel}] Successfully recreated asset index file with {Count} entries pointing to local logos at {Path}",
"AssetsChannel", indexAssets.Count, filePath);
await _finlyticLogger.LogInfoAsync(SettingKeys.AssetsChannel, "[AssetsIndexService] Successfully recreated asset index file with {Count} entries pointing to local logos at {Path}",
indexAssets.Count, filePath);
}
catch (IOException ex)
{
_logger.LogError(ex, "[{Channel}] Disk I/O error occurred while writing the asset index file.", "AssetsChannel");
await _finlyticLogger.LogErrorAsync(SettingKeys.AssetsChannel, ex, "[AssetsIndexService] Disk I/O error occurred while writing the asset index file.");
throw;
}
catch (JsonException ex)
{
_logger.LogError(ex, "[{Channel}] Failed to serialize the asset index data to JSON.", "AssetsChannel");
await _finlyticLogger.LogErrorAsync(SettingKeys.AssetsChannel, ex, "[AssetsIndexService] Failed to serialize the asset index data to JSON.");
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "[{Channel}] An unexpected error occurred while recreating the asset index file.", "AssetsChannel");
await _finlyticLogger.LogErrorAsync(SettingKeys.AssetsChannel, ex, "[AssetsIndexService] An unexpected error occurred while recreating the asset index file.");
throw;
}
}
@@ -131,13 +129,13 @@ public class AssetsIndexService : IAssetsIndexService
{
byte[] data = await response.Content.ReadAsByteArrayAsync(cancellationToken);
await File.WriteAllBytesAsync(filePath, data, cancellationToken);
_logger.LogInformation("[{Channel}] Successfully saved logo SVG for ISIN {Isin} to {Path} on demand", "AssetsChannel", cleanIsin, filePath);
await _finlyticLogger.LogInfoAsync(SettingKeys.AssetsChannel, "[AssetsIndexService] Successfully saved logo SVG for ISIN {Isin} to {Path} on demand", cleanIsin, filePath);
return filePath;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "[{Channel}] Failed to download logo for ISIN {Isin} from {Url}", "AssetsChannel", cleanIsin, targetUrl);
await _finlyticLogger.LogWarningAsync(SettingKeys.AssetsChannel, ex, "[AssetsIndexService] Failed to download logo for ISIN {Isin} from {Url}", cleanIsin, targetUrl);
}
return null;