feat(email): add durable send ledger
CI and Deploy / test (pull_request) Failing after 1m21s
CI and Deploy / deploy (pull_request) Has been skipped

Tracks only idempotency and delivery metadata; recipient, subject, and body are excluded. No provider send path is enabled.
This commit is contained in:
cesnimda
2026-08-09 23:32:27 +02:00
parent 3a7e4f1088
commit 653f011be2
8 changed files with 3077 additions and 0 deletions
+21
View File
@@ -61,6 +61,7 @@ namespace JobTrackerApi.Data
public DbSet<InterviewPrepItem> InterviewPrepItems => Set<InterviewPrepItem>();
public DbSet<UserOperation> UserOperations => Set<UserOperation>();
public DbSet<UserNotification> UserNotifications => Set<UserNotification>();
public DbSet<EmailSendAttempt> EmailSendAttempts => Set<EmailSendAttempt>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
@@ -259,6 +260,26 @@ namespace JobTrackerApi.Data
.HasForeignKey<UserNotification>(x => x.OperationId)
.OnDelete(DeleteBehavior.SetNull);
modelBuilder.Entity<EmailSendAttempt>()
.HasQueryFilter(x => CurrentUserId != null && x.OwnerUserId == CurrentUserId);
modelBuilder.Entity<EmailSendAttempt>().Property(x => x.OwnerUserId).HasMaxLength(255);
modelBuilder.Entity<EmailSendAttempt>().Property(x => x.Provider).HasMaxLength(32);
modelBuilder.Entity<EmailSendAttempt>().Property(x => x.ClientRequestId).HasMaxLength(128);
modelBuilder.Entity<EmailSendAttempt>().Property(x => x.PayloadHash).HasMaxLength(64);
modelBuilder.Entity<EmailSendAttempt>().Property(x => x.Status).HasMaxLength(32);
modelBuilder.Entity<EmailSendAttempt>().Property(x => x.ProviderMessageId).HasMaxLength(256);
modelBuilder.Entity<EmailSendAttempt>().Property(x => x.FailureCategory).HasMaxLength(64);
modelBuilder.Entity<EmailSendAttempt>()
.HasIndex(x => new { x.OwnerUserId, x.ClientRequestId })
.IsUnique();
modelBuilder.Entity<EmailSendAttempt>()
.HasIndex(x => new { x.OwnerUserId, x.CreatedAtUtc });
modelBuilder.Entity<EmailSendAttempt>()
.HasOne(x => x.JobApplication)
.WithMany()
.HasForeignKey(x => x.JobApplicationId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<TailoredCvDraft>()
.HasQueryFilter(x => CurrentUserId != null && x.OwnerUserId == CurrentUserId);