26b13f6dbf
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>
1.7 KiB
1.7 KiB
email-api — Internal Email Sending Service
Internal only. Reachable at http://email-api:8080 within myai-network. Not exposed to the internet.
Responsibilities
- Accepts
POST /api/email/sendrequests from internal services (api,cv-search-job) - Wraps the provided HTML body fragment in a branded HTML shell (blue header, white card, grey footer)
- Sends the email via SMTP using MailKit
- Attaches files from the shared
Filesvolume whenAttachmentPathis provided - Protected by
X-Internal-Api-KeyviaUseInternalApiKeyProtection()
Key route
| Method | Route | Description |
|---|---|---|
| POST | /api/email/send |
Send an HTML email. Returns 204 No Content. |
Request body (SendEmailRequest)
| Field | Required | Notes |
|---|---|---|
To |
✅ | List of recipient addresses |
ReplyTo |
❌ | Optional reply-to address |
Subject |
✅ | Plain text (service prepends [ENV_NAME]) |
HtmlBody |
✅ | HTML fragment — wrapped in branded shell by this service |
AttachmentPath |
❌ | Path relative to FileStorage:Path, e.g. "{cvDocumentId}.pdf" |
Consumers
api— viaIEmailApiClientRefit interface (contact, subscribe, file-download, match emails)cv-search-job— viaIEmailApiClientRefit interface (job search results email)
Settings
| Section | Env var | Notes |
|---|---|---|
Smtp |
Smtp__Host, Smtp__Username, Smtp__Password, etc. |
SMTP server config — only configured here, not in consumers |
FileStorage |
FileStorage__Path |
Must match the shared Files volume mount path |
InternalApi |
EmailApi__InternalApiKey |
API key enforced on every request |