From e7ca6043b7988dc66cd317ba6656c17f23366140 Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 28 May 2026 08:43:07 +0300 Subject: [PATCH] feat(api): wire IEmailTemplateService; replace Contact:ToEmail with OperatorCopy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add ProjectReference to email-api-data - Register EmailApiDbContext (no migrate — email-api owns migrations) - Register IEmailTemplateRepository (scoped) and IEmailTemplateService (singleton) - EmailApiEmailSender: replace ITemplateService with IEmailTemplateService for all email.* template rendering (match body/subject/footer) - SendMatchAsync: replace _contact.ToEmail operator copy with GetOperatorCopy("email.match.subject", "en") from DB template Co-Authored-By: Claude Sonnet 4.6 --- Apis/api/Program.cs | 17 +++++++++++++++++ Apis/api/Services/EmailApiEmailSender.cs | 22 ++++++++++++---------- Apis/api/api.csproj | 1 + 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/Apis/api/Program.cs b/Apis/api/Program.cs index 5cf8974..295c83f 100644 --- a/Apis/api/Program.cs +++ b/Apis/api/Program.cs @@ -1,6 +1,10 @@ using System.Reflection; using Api.Services; using Api.Services.Contracts; +using EmailApi.Data; +using EmailApi.Data.Repositories; +using EmailApi.Data.Repositories.Contracts; +using EmailApi.Data.Services; using EmailApi.Models.Clients; using EmailApi.Models.Settings; using Microsoft.EntityFrameworkCore; @@ -47,6 +51,19 @@ try }); builder.Services.AddSingleton(); + builder.Services.AddDbContext(options => + { + var connectionString = builder.Services.GetConfiguredDbConnectionString(builder.Configuration); + options.UseSqlServer(connectionString, sql => + { + sql.MigrationsHistoryTable(EmailApiDbContext.MigrationTableName, EmailApiDbContext.SchemaName); + sql.MigrationsAssembly("email-api-data"); + }); + }); + + builder.Services.AddScoped(); + builder.Services.AddSingleton(); + builder.Services.AddHttpClient(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/Apis/api/Services/EmailApiEmailSender.cs b/Apis/api/Services/EmailApiEmailSender.cs index 6a76466..7d2a5a3 100644 --- a/Apis/api/Services/EmailApiEmailSender.cs +++ b/Apis/api/Services/EmailApiEmailSender.cs @@ -1,11 +1,11 @@ using Api.Services.Contracts; using CvMatcher.Models.Responses; +using EmailApi.Data.Services; using EmailApi.Models.Clients; using EmailApi.Models.Requests; using Microsoft.Extensions.Options; using Models.Requests; using Models.Settings; -using MyAi.Data.Services; namespace Api.Services; @@ -15,7 +15,7 @@ public sealed class EmailApiEmailSender : IEmailSender private readonly ContactSettings _contact; private readonly SubscribeSettings _subscribe; private readonly FileStorageSettings _fileStorage; - private readonly ITemplateService _templates; + private readonly IEmailTemplateService _emailTemplates; private readonly ILogger _log; public EmailApiEmailSender( @@ -23,14 +23,14 @@ public sealed class EmailApiEmailSender : IEmailSender IOptions contact, IOptions subscribe, IOptions fileStorage, - ITemplateService templates, + IEmailTemplateService emailTemplates, ILogger log) { _emailApi = emailApi; _contact = contact.Value; _subscribe = subscribe.Value; _fileStorage = fileStorage.Value; - _templates = templates; + _emailTemplates = emailTemplates; _log = log; } @@ -148,13 +148,15 @@ public sealed class EmailApiEmailSender : IEmailSender public async Task SendMatchAsync(string? explicitTo, string subject, string body, string? attachmentPath, CancellationToken ct) { + var operatorCopy = _emailTemplates.GetOperatorCopy("email.match.subject", "en"); + var recipients = new List(); if (!string.IsNullOrWhiteSpace(explicitTo)) recipients.Add(explicitTo); - if (!string.IsNullOrWhiteSpace(_contact.ToEmail) && - !recipients.Any(x => string.Equals(x, _contact.ToEmail, StringComparison.OrdinalIgnoreCase))) - recipients.Add(_contact.ToEmail); + if (!string.IsNullOrWhiteSpace(operatorCopy) && + !recipients.Any(x => string.Equals(x, operatorCopy, StringComparison.OrdinalIgnoreCase))) + recipients.Add(operatorCopy); if (recipients.Count == 0) { @@ -199,7 +201,7 @@ public sealed class EmailApiEmailSender : IEmailSender string.Join("", result.Recommendations.Select(r => $"
  • {r}
  • ")) + "" : "

    "; - var body = _templates.Render("email.match.body", language, + var body = _emailTemplates.Render("email.match.body", language, ("cvDocumentId", cvDocumentId), ("jobLabel", jobLabel ?? "N/A"), ("jobUrl", result.JobUrl ?? "N/A"), @@ -211,7 +213,7 @@ public sealed class EmailApiEmailSender : IEmailSender if (!string.IsNullOrWhiteSpace(jobSearchLink)) { - body += _templates.Render("email.match.job-search-footer", language, + body += _emailTemplates.Render("email.match.job-search-footer", language, ("jobSearchLink", jobSearchLink), ("expiryDays", expiryDays.ToString())); } @@ -220,7 +222,7 @@ public sealed class EmailApiEmailSender : IEmailSender } public string BuildMatchEmailSubject(int score, string? jobLabel, string language) => - _templates.Render("email.match.subject", language, + _emailTemplates.Render("email.match.subject", language, ("score", score.ToString()), ("jobLabel", jobLabel ?? "Job")); } diff --git a/Apis/api/api.csproj b/Apis/api/api.csproj index f91a051..96be1e8 100644 --- a/Apis/api/api.csproj +++ b/Apis/api/api.csproj @@ -36,6 +36,7 @@ +