feat: extract email sending into dedicated email-api service #23

Merged
gelu merged 5 commits from feature/email-api into main 2026-05-27 13:34:04 +00:00
Owner

What

Centralise all SMTP email sending into a new internal email-api service. api and cv-search-job stop using MailKit directly and call email-api via a Refit client.

Why

Email-sending logic was duplicated across two services (api/Services/SmtpEmailSender.cs and cv-search-job/Services/CvSearchEmailSender.cs), each managing its own SMTP connection. Centralising it means one SMTP config point, one place to update sending logic, and easier future changes (e.g. provider swap).

Changes

  • New Apis/email-api-models/SendEmailRequest, IEmailApiClient (Refit), EmailApiSettings
  • New Apis/email-api/EmailController, SmtpEmailDispatcher, Program.cs, Dockerfile; wraps any HtmlBody fragment in a branded HTML shell (blue #2c5282 header, white card, grey footer); handles optional file attachments from the shared Files volume
  • api — rename SmtpEmailSenderEmailApiEmailSender; switch from MailKit to IEmailApiClient; HTML list formatting for match email strengths/gaps/recommendations
  • cv-search-job — switch CvSearchEmailSender to call IEmailApiClient; pass relative attachment filename instead of full path
  • Migration UpdateEmailTemplatesToHtml — upgrade 8 DB templates from plain text to styled HTML (inline CSS, Gmail-compatible)
  • docker-compose.yml — add email-api service (internal, no ports); remove Smtp__* from api + cv-search-job; add EmailApi__* vars + depends_on: email-api
  • myAi.sln + CLAUDE.md — updated layout, dependency diagram, internal API key table

Testing

  • dotnet build myAi.sln — 0 errors, 0 warnings
  • MailKit referenced only by email-api.csproj
  • Smtp__* env vars present only in email-api service block

Risk Assessment

  • Breaking changes? No public API surface changes
  • Performance impact: negligible (one extra internal HTTP hop per email)
  • Related issues: Closes #22

Checklist

  • Build passing (0 errors, 0 warnings)
  • No merge conflicts
  • CLAUDE.md updated

🤖 Generated with Claude Code

## What Centralise all SMTP email sending into a new internal `email-api` service. `api` and `cv-search-job` stop using MailKit directly and call `email-api` via a Refit client. ## Why Email-sending logic was duplicated across two services (`api/Services/SmtpEmailSender.cs` and `cv-search-job/Services/CvSearchEmailSender.cs`), each managing its own SMTP connection. Centralising it means one SMTP config point, one place to update sending logic, and easier future changes (e.g. provider swap). ## Changes - **New `Apis/email-api-models/`** — `SendEmailRequest`, `IEmailApiClient` (Refit), `EmailApiSettings` - **New `Apis/email-api/`** — `EmailController`, `SmtpEmailDispatcher`, `Program.cs`, `Dockerfile`; wraps any `HtmlBody` fragment in a branded HTML shell (blue `#2c5282` header, white card, grey footer); handles optional file attachments from the shared Files volume - **`api`** — rename `SmtpEmailSender` → `EmailApiEmailSender`; switch from MailKit to `IEmailApiClient`; HTML list formatting for match email strengths/gaps/recommendations - **`cv-search-job`** — switch `CvSearchEmailSender` to call `IEmailApiClient`; pass relative attachment filename instead of full path - **Migration `UpdateEmailTemplatesToHtml`** — upgrade 8 DB templates from plain text to styled HTML (inline CSS, Gmail-compatible) - **`docker-compose.yml`** — add `email-api` service (internal, no ports); remove `Smtp__*` from `api` + `cv-search-job`; add `EmailApi__*` vars + `depends_on: email-api` - **`myAi.sln`** + **`CLAUDE.md`** — updated layout, dependency diagram, internal API key table ## Testing - `dotnet build myAi.sln` — 0 errors, 0 warnings - MailKit referenced only by `email-api.csproj` - `Smtp__*` env vars present only in `email-api` service block ## Risk Assessment - Breaking changes? No public API surface changes - Performance impact: negligible (one extra internal HTTP hop per email) - Related issues: Closes #22 ## Checklist - [x] Build passing (0 errors, 0 warnings) - [x] No merge conflicts - [x] CLAUDE.md updated 🤖 Generated with [Claude Code](https://claude.com/claude-code)
gelu added 5 commits 2026-05-27 13:31:29 +00:00
New internal service that centralises SMTP email sending.
- email-api-models: SendEmailRequest DTO, IEmailApiClient Refit interface, EmailApiSettings
- email-api: SmtpEmailDispatcher (MailKit), EmailController (POST /api/email/send),
  branded HTML shell wrapper, shared-Files-volume attachment support
- Protected by X-Internal-Api-Key via UseInternalApiKeyProtection()
- No exposed Docker port — internal network only (http://email-api:8080)

Closes #22

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- EmailApiEmailSender calls email-api via IEmailApiClient Refit client
- HTML bodies built inline for contact/subscribe/file-download emails
- match and job-search emails use DB templates (rendered in caller)
- SmtpSettings moved from api-models to email-api (kept in Models.Settings namespace)
- MailKit removed from api.csproj
- SmtpEmailSender deleted; IEmailSender interface unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CvSearchEmailSender now injects IEmailApiClient instead of IConfiguration+MailKit
- BuildBody updated to produce styled HTML job cards
- CvSearchJobTask passes only filename (not full path) to email sender
- IEmailApiClient registered in Program.cs with EmailApi:BaseUrl + InternalApiKey
- MailKit removed from cv-search-job.csproj

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Upgrades 8 email body templates from plain text to styled HTML.
Templates: email.match.body, email.match.job-search-footer,
email.search-results.body, email.search-results.empty (en + ro each).
All use inline CSS only (Gmail-compatible). Branded #2c5282 accent.

Closes #22

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- docker-compose: add email-api service (internal, no ports)
  with Smtp__* + FileStorage__Path + Files volume mount
- api + cv-search-job: remove Smtp__* vars, add EmailApi__BaseUrl
  and EmailApi__InternalApiKey; add depends_on: email-api
- .sln: move email-api-models to Models virtual folder
- CLAUDE.md: add email-api/email-api-models to layout, update
  service dependency diagram and internal API key table

Closes #22

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
claude approved these changes 2026-05-27 13:33:08 +00:00
gelu merged commit e260982a91 into main 2026-05-27 13:34:04 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: AI/myAi#23