Add debug export functionality for channel analysis
ci / build-backend (push) Successful in 1m16s
ci / build-frontend (push) Successful in 42s
ci / tests (push) Successful in 1m24s
ci / sonar (push) Successful in 5m9s

Implemented a new endpoint for exporting debug data related to channel scheduling, allowing users to download an archive containing channel settings, slot states, and trace information. Updated the frontend to include a button for triggering the export, along with necessary API adjustments for handling the download. Enhanced localization strings to support the new debug export feature in both English and Russian. Updated .gitignore to include debug export files while ensuring the directory structure is maintained for development.
This commit is contained in:
Leonid Pershin
2026-07-31 02:58:17 +03:00
parent 01ba48e163
commit 9cd364b174
21 changed files with 1055 additions and 7 deletions
@@ -7,6 +7,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using TeleWave.Application.Broadcast.Bumpers;
using TeleWave.Application.Broadcast.Scheduling;
using TeleWave.Application.Common;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Streaming;
using TeleWave.Domain.Broadcast.Scheduling;
@@ -18,6 +19,7 @@ using TeleWave.Infrastructure.Metadata;
using TeleWave.Infrastructure.Persistence;
using TeleWave.Infrastructure.Settings;
using TeleWave.Infrastructure.Streaming;
using TeleWave.Infrastructure.Support;
namespace TeleWave.Infrastructure;
@@ -149,6 +151,10 @@ public static class DependencyInjection
private static void AddMedia(IServiceCollection services, IConfiguration configuration)
{
services.Configure<StorageOptions>(configuration.GetSection(StorageOptions.SectionName));
services.Configure<DebugExportOptions>(
configuration.GetSection(DebugExportOptions.SectionName)
);
services.AddSingleton<IDebugExportStore, DebugExportStore>();
services.Configure<MediaOptions>(configuration.GetSection(MediaOptions.SectionName));
services.AddSingleton<MediaPathResolver>();
@@ -0,0 +1,78 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using TeleWave.Application.Common;
using TeleWave.Application.Common.Interfaces;
namespace TeleWave.Infrastructure.Support;
/// <summary>
/// Складывает копии отладочных дампов в настроенный каталог и подрезает его до последних N архивов.
///
/// Ошибки записи только логируются: дамп к этому моменту уже собран и уходит в браузер, и валить
/// запрос из-за того, что папка недоступна, значит терять данные ровно тогда, когда они нужны.
/// </summary>
public sealed class DebugExportStore(
IOptions<DebugExportOptions> options,
ILogger<DebugExportStore> logger
) : IDebugExportStore
{
private const string ArchivePattern = "*.zip";
private readonly DebugExportOptions _options = options.Value;
public async Task<string?> SaveAsync(
string fileName,
byte[] content,
CancellationToken cancellationToken
)
{
if (string.IsNullOrWhiteSpace(_options.ExportDirectory))
return null;
try
{
var directory = Path.GetFullPath(_options.ExportDirectory);
Directory.CreateDirectory(directory);
var path = Path.Combine(directory, Path.GetFileName(fileName));
await File.WriteAllBytesAsync(path, content, cancellationToken);
Prune(directory);
return path;
}
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException)
{
logger.LogWarning(
exception,
"Не удалось сохранить отладочный дамп в {Directory}",
_options.ExportDirectory
);
return null;
}
}
/// <summary>Оставляет последние <see cref="DebugExportOptions.KeepLast"/> архивов по времени записи.</summary>
private void Prune(string directory)
{
var keep = Math.Max(1, _options.KeepLast);
var stale = new DirectoryInfo(directory)
.GetFiles(ArchivePattern)
.OrderByDescending(file => file.LastWriteTimeUtc)
.Skip(keep)
.ToList();
foreach (var file in stale)
{
try
{
file.Delete();
}
catch (Exception exception)
when (exception is IOException or UnauthorizedAccessException)
{
// Файл держит открытым архиватор или антивирус — не повод срывать экспорт.
logger.LogDebug(exception, "Не удалось удалить старый дамп {File}", file.FullName);
}
}
}
}