feat(email): route SendGrid sends through EU data residency#619
Conversation
Add SENDGRID_REGION (default "eu") and call set_sendgrid_data_residency() so the app's transactional email (email.py -> SendGrid HTTP API) routes through api.eu.sendgrid.com, keeping recipient PII and content in EU data centers. Requires SENDGRID_API_KEY to belong to an EU regional subuser. Note: the echo-directus container sends its own emails via SMTP and must separately move to smtp.eu.sendgrid.net with the EU subuser key (infra). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 56 minutes and 27 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughSendGrid data residency region is now configurable. The ChangesSendGrid Data Residency Region Configuration
🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Note that email.py (SendGrid HTTP API) and the echo-directus container (SendGrid SMTP) are separate paths with separate keys/config, and that EU data residency must be set on each independently. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@echo/server/dembrane/settings.py`:
- Around line 330-334: The sendgrid_region Field currently accepts arbitrary
strings; add normalization and validation so only allowed regions are used:
define a SendGridRegion enum (e.g., values 'eu' and 'us') or add a Pydantic
validator named normalize_sendgrid_region for the sendgrid_region field that
strips whitespace, lowercases the input, maps common synonyms if desired, and
raises a ValueError for invalid values; update the sendgrid_region Field
declaration (the sendgrid_region symbol and its Field/AliasChoices usage) to use
the enum or validated value so typos/casing like "EU " or "euu" are
rejected/normalized at config load.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 1208c7ba-c46b-4f19-82f6-3bc68095e190
📒 Files selected for processing (3)
echo/server/.env.sampleecho/server/dembrane/email.pyecho/server/dembrane/settings.py
| sendgrid_region: str = Field( | ||
| default="eu", | ||
| alias="SENDGRID_REGION", | ||
| validation_alias=AliasChoices("SENDGRID_REGION", "EMAIL__SENDGRID_REGION"), | ||
| ) |
There was a problem hiding this comment.
Constrain and normalize sendgrid_region at config load.
Line 330 currently accepts arbitrary strings; typos/casing like EU or euu will flow into the SendGrid routing call and can break all sends (downstream queue jobs then retry/fail noisily).
💡 Suggested fix
class EmailSettings(BaseSettings):
@@
- sendgrid_region: str = Field(
+ sendgrid_region: Literal["eu", "global"] = Field(
default="eu",
alias="SENDGRID_REGION",
validation_alias=AliasChoices("SENDGRID_REGION", "EMAIL__SENDGRID_REGION"),
)
+
+ `@field_validator`("sendgrid_region", mode="before")
+ `@classmethod`
+ def normalize_sendgrid_region(cls, value: Any) -> str:
+ region = str(value or "eu").strip().lower()
+ if region not in {"eu", "global"}:
+ raise ValueError("SENDGRID_REGION must be 'eu' or 'global'")
+ return region🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@echo/server/dembrane/settings.py` around lines 330 - 334, The sendgrid_region
Field currently accepts arbitrary strings; add normalization and validation so
only allowed regions are used: define a SendGridRegion enum (e.g., values 'eu'
and 'us') or add a Pydantic validator named normalize_sendgrid_region for the
sendgrid_region field that strips whitespace, lowercases the input, maps common
synonyms if desired, and raises a ValueError for invalid values; update the
sendgrid_region Field declaration (the sendgrid_region symbol and its
Field/AliasChoices usage) to use the enum or validated value so typos/casing
like "EU " or "euu" are rejected/normalized at config load.
What
Routes the app's transactional email through SendGrid's EU data residency region so recipient PII and email content stay in EU data centers (GDPR).
SENDGRID_REGIONsetting (EmailSettings), default"eu".email.pycallsset_sendgrid_data_residency(region)before sending, switching the client host toapi.eu.sendgrid.comwhen region !=global.SENDGRID_REGIONinserver/.env.sample.Why
dembrane is moving email to EU residency. An EU regional subuser (
sameer-eu, EU IP159.183.46.124) and thedembrane.comdomain authentication (EU CNAMEs in Cloudflare) are already set up. This wires the app sender (email.py→ SendGrid HTTP API) to use it.Verified: a live test send via
api.eu.sendgrid.comwith the EU subuser key returns202, and the residency call flips the client host correctly.Requires (infra, not in this PR)
SENDGRID_API_KEYmust be set to the EU regional subuser key (restricted Mail Send) in theecho-api+echo-workersealed secrets. A global-account key won't be EU-resident even on the EU host.echo-directuscontainer sends its own emails via SMTP. To make those EU-resident too, setEMAIL_SMTP_HOST=smtp.eu.sendgrid.netandEMAIL_SMTP_PASSWORD=<EU subuser key>on that deployment.Scope note
This is backend/infra email routing. It is not related to the email-template mobile-layout work in #608 / ECHO-832, despite both touching "email".
🤖 Generated with Claude Code
Summary by CodeRabbit