diff --git a/calls/backend/Calls.Api.Tests/RecordingTests.cs b/calls/backend/Calls.Api.Tests/RecordingTests.cs index 979c94e..9601ed7 100644 --- a/calls/backend/Calls.Api.Tests/RecordingTests.cs +++ b/calls/backend/Calls.Api.Tests/RecordingTests.cs @@ -243,6 +243,25 @@ public sealed class ProcessorTests : IDisposable Assert.False(Directory.Exists(dir)); } + [Fact] + public async Task TestWorkspace_NotPublished_FilesDeleted() + { + var c = _calls.Create("ws-t", "test-agent-a"); + _calls.RecordingStarted(c.Id, c.Id); + _time.Advance(TimeSpan.FromMinutes(5)); + _calls.Seen(c.Id, []); + _calls.Ended(c.Id, _calls.Now, true); + var dir = Path.Combine(_root, "rec", c.Id); + Directory.CreateDirectory(dir); + File.WriteAllText(Path.Combine(dir, "meta.json"), "{\"audio\":\"audio.mp3\",\"mime\":\"audio/mpeg\",\"duration\":300,\"bytes\":1,\"parts\":[]}"); + File.WriteAllText(Path.Combine(dir, "ready"), ""); + var http = Http(); + await Proc(http, Opt()).RunOnceAsync(default); + Assert.Equal("skipped", _calls.Get(c.Id)!.Status); + Assert.False(Directory.Exists(dir)); + Assert.Empty(http.Log); + } + [Fact] public async Task ShortCall_Skipped_NoPage_FilesDeleted() { diff --git a/calls/backend/Calls.Api/Program.cs b/calls/backend/Calls.Api/Program.cs index 10e921e..f46d9e3 100644 --- a/calls/backend/Calls.Api/Program.cs +++ b/calls/backend/Calls.Api/Program.cs @@ -20,6 +20,7 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddHttpClient("jitsi", c => c.Timeout = TimeSpan.FromSeconds(5)); +builder.Services.AddHttpClient("jibri-stop", c => c.Timeout = TimeSpan.FromSeconds(60)); builder.Services.AddHttpClient("openai", c => c.Timeout = TimeSpan.FromMinutes(10)); builder.Services.AddHttpClient("storage", c => c.Timeout = TimeSpan.FromMinutes(5)); builder.Services.AddHttpClient("wiki", c => c.Timeout = TimeSpan.FromSeconds(30)); diff --git a/calls/backend/Calls.Api/Recording/JitsiClients.cs b/calls/backend/Calls.Api/Recording/JitsiClients.cs index e9ce2e9..893a975 100644 --- a/calls/backend/Calls.Api/Recording/JitsiClients.cs +++ b/calls/backend/Calls.Api/Recording/JitsiClients.cs @@ -77,7 +77,10 @@ public sealed class JibriClient(IHttpClientFactory http, RecordingOptions opt) { try { - using var r = await http.CreateClient("jitsi").PostAsync($"{opt.JibriUrl}/jibri/api/v1.0/stopService", null, ct); + // stopService ждёт, пока Jibri закроет ffmpeg и Chrome — это дольше обычного таймаута клиента. + using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); + cts.CancelAfter(TimeSpan.FromSeconds(60)); + using var r = await http.CreateClient("jibri-stop").PostAsync($"{opt.JibriUrl}/jibri/api/v1.0/stopService", null, cts.Token); return r.IsSuccessStatusCode; } catch (Exception e) when (e is HttpRequestException or TaskCanceledException) { return false; } diff --git a/calls/backend/Calls.Api/Recording/RecordingProcessor.cs b/calls/backend/Calls.Api/Recording/RecordingProcessor.cs index e462506..93ed95b 100644 --- a/calls/backend/Calls.Api/Recording/RecordingProcessor.cs +++ b/calls/backend/Calls.Api/Recording/RecordingProcessor.cs @@ -82,6 +82,15 @@ public sealed class RecordingProcessor( return; } + // Тестовые комнаты test-* (служебный /api/admin/test-link) — только проверка записи: наружу не публикуем. + if (call.WorkspaceId.StartsWith("test-", StringComparison.Ordinal)) + { + DeleteDir(dir); + calls.SetStatus(call.Id, "skipped", "тестовий воркспейс: запис перевірено, не публікується"); + log.LogInformation("Дзвінок {C}: тестовий воркспейс {W} — не публікується, файли видалено", call.Id, call.WorkspaceId); + return; + } + // Без ключа OpenAI и пользователя CRM ничего наружу не отправляем: запись ждёт на диске. if (!opt.HasOpenAi || !opt.HasCrmUser || opt.SupabaseUrl.Length == 0) {