Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
Task<List<User>> GetUsersByAffiliateId(string affiliateId) => throw new NotImplementedException();
Task<User?> GetUserByUserName(string userName) => throw new NotImplementedException();
Task UpdateUserName(string userId, string userName) => throw new NotImplementedException();
Task<Dashboard?> GetDashboard(string id = null) => throw new NotImplementedException();

Check warning on line 51 in src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Cannot convert null literal to non-nullable reference type.

Check warning on line 51 in src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Cannot convert null literal to non-nullable reference type.

Check warning on line 51 in src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Cannot convert null literal to non-nullable reference type.

Check warning on line 51 in src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Cannot convert null literal to non-nullable reference type.

Check warning on line 51 in src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Cannot convert null literal to non-nullable reference type.

Check warning on line 51 in src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Cannot convert null literal to non-nullable reference type.
Task CreateUser(User user) => throw new NotImplementedException();
Task UpdateExistUser(string userId, User user) => throw new NotImplementedException();
Task UpdateUserVerified(string userId) => throw new NotImplementedException();
Expand Down Expand Up @@ -201,6 +201,9 @@
#region Log Cleanup
Task<int> DeleteOldConversationLogs(int retentionDays, int batchSize)
=> throw new NotImplementedException();

Task<int> DeleteOldInstructionLogs(int retentionDays, int batchSize)
=> throw new NotImplementedException();
#endregion

#region Instruction Log
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ public async Task<int> DeleteOldConversationLogs(int retentionDays, int batchSiz
// as it's typically used for local dev. Implementing it would require iterating all conversations.
return await Task.FromResult(0);
}

public async Task<int> DeleteOldInstructionLogs(int retentionDays, int batchSize = 2000)
{
// For file repository, keep the same cleanup behavior as conversation logs.
return await Task.FromResult(0);
}
#endregion

#region Instruction Log
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ public static IServiceCollection AddCrontabServices(this IServiceCollection serv
services.AddScoped<ICrontabHook, ConversationLogCleanupCrontabHook>();
services.AddScoped<ICrontabSource, ConversationLogCleanupRuleTrigger>();
services.AddScoped<IRuleTrigger, ConversationLogCleanupRuleTrigger>();

services.AddScoped<ICrontabHook, InstructionLogCleanupCrontabHook>();
services.AddScoped<ICrontabSource, InstructionLogCleanupRuleTrigger>();
services.AddScoped<IRuleTrigger, InstructionLogCleanupRuleTrigger>();
return services;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using BotSharp.Abstraction.Crontab;
using BotSharp.Abstraction.Crontab.Models;
using BotSharp.Abstraction.Repositories;
using BotSharp.OpenAPI.RuleTriggers;
using Microsoft.Extensions.Hosting;

namespace BotSharp.OpenAPI.Hooks
{
public class InstructionLogCleanupCrontabHook : ICrontabHook
{
private readonly ConversationSetting _settings;
private readonly IBotSharpRepository _db;
private readonly ILogger<InstructionLogCleanupCrontabHook> _logger;
private readonly IHostApplicationLifetime _appLifetime;

public string[]? Triggers => new[] { nameof(InstructionLogCleanupRuleTrigger) };

public InstructionLogCleanupCrontabHook(
ConversationSetting settings,
IBotSharpRepository db,
ILogger<InstructionLogCleanupCrontabHook> logger,
IHostApplicationLifetime appLifetime)
{
_settings = settings;
_db = db;
_logger = logger;
_appLifetime = appLifetime;
}

public async Task OnCronTriggered(CrontabItem item)
{
var cleanSetting = _settings.CleanSetting;

if (cleanSetting == null || !cleanSetting.Enable || cleanSetting.LogRetentionDays <= 0) return;

int totalDeleted = 0;
var cancellationToken = _appLifetime.ApplicationStopping;

while (!cancellationToken.IsCancellationRequested)
{
var deletedCount = await _db.DeleteOldInstructionLogs(cleanSetting.LogRetentionDays, cleanSetting.LogBatchSize);
if (deletedCount == 0) break;

totalDeleted += deletedCount;
_logger.LogInformation($"Cleaned {deletedCount} instruction logs older than {cleanSetting.LogRetentionDays} days in this batch.");

try
{
// Sleep slightly to yield database resources, will throw TaskCanceledException on shutdown
await Task.Delay(1000, cancellationToken);
}
catch (OperationCanceledException)
{
_logger.LogWarning("Instruction log cleanup was interrupted due to application shutdown.");
break;
}
}

if (totalDeleted > 0)
{
_logger.LogInformation($"Successfully cleaned a total of {totalDeleted} instruction logs older than {cleanSetting.LogRetentionDays} days.");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using BotSharp.Abstraction.Crontab;
using BotSharp.Abstraction.Crontab.Models;
using BotSharp.Abstraction.Rules;

namespace BotSharp.OpenAPI.RuleTriggers
{
public class InstructionLogCleanupRuleTrigger : IRuleTrigger, ICrontabSource
{
public string Channel => ConversationChannel.Crontab;
public string Name => nameof(InstructionLogCleanupRuleTrigger);
public string EntityType { get; set; } = string.Empty;
public string EntityId { get; set; } = string.Empty;

public CrontabItem GetCrontabItem()
{
return new CrontabItem
{
Title = nameof(InstructionLogCleanupRuleTrigger),
Description = "Clean up old instruction logs daily",
Cron = "0 6 * * *", // Run at 6:00 AM UTC (Midnight Chicago Standard Time)
TriggerType = CronTabItemTriggerType.MessageQueue
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,23 @@ public async Task<int> DeleteOldConversationLogs(int retentionDays, int batchSiz

return (int)(contentDeletedCount + stateDeletedCount);
}

public async Task<int> DeleteOldInstructionLogs(int retentionDays, int batchSize)
{
if (retentionDays <= 0) return 0;
var threshold = DateTime.UtcNow.AddDays(-retentionDays);

var filter = Builders<InstructionLogDocument>.Filter.Lt(x => x.CreatedTime, threshold);
var docsToDelete = await _dc.InstructionLogs.Find(filter).Limit(batchSize).Project(x => x.Id).ToListAsync();
if (!docsToDelete.Any())
{
return 0;
}

var deleteFilter = Builders<InstructionLogDocument>.Filter.In(x => x.Id, docsToDelete);
var deleted = await _dc.InstructionLogs.DeleteManyAsync(deleteFilter);
return (int)deleted.DeletedCount;
}
#endregion

#region Instruction Log
Expand Down
Loading