Skip to content

fix(api): make OTP email sender configurable via env#69

Merged
Kingvic300 merged 1 commit into
mainfrom
fix/56-email-sender
Jun 7, 2026
Merged

fix(api): make OTP email sender configurable via env#69
Kingvic300 merged 1 commit into
mainfrom
fix/56-email-sender

Conversation

@Kingvic300

@Kingvic300 Kingvic300 commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Summary

The OTP email sender was hardcoded to no-reply@txio-backend.com. Brevo only sends from verified senders/domains, so unless a deployment owns and verifies that exact domain, every OTP send is rejected — breaking registration confirmation, login OTP, and password reset, surfacing only as a generic ExternalService error.

Changes

  • email_service.rs: read EMAIL_FROM and EMAIL_FROM_NAME from the environment, defaulting to the previous values (no behavior change when unset).
  • .env.example: document the new vars and the Brevo verification requirement.

Testing

  • cargo check -p txio-api passes.

Closes #56

Summary by CodeRabbit

  • Chores
    • Made email sender configuration (sender address and display name for OTP emails) customizable via environment variables with sensible defaults, replacing hardcoded values.

The OTP sender was hardcoded to no-reply@txio-backend.com. Brevo only
sends from verified senders, so unless a deployment happens to own and
verify that exact domain, every OTP send fails (breaking registration,
login OTP, and password reset). Read EMAIL_FROM / EMAIL_FROM_NAME from
the environment, defaulting to the previous values, and document them in
.env.example.

Closes #56
@vercel

vercel Bot commented Jun 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
txio Ready Ready Preview, Comment Jun 7, 2026 11:49am

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR externalizes the hardcoded OTP email sender identity to environment variables. .env.example adds EMAIL_FROM and EMAIL_FROM_NAME with defaults and clarifies the Brevo sender-verification requirement. The email service reads these variables (with fallback defaults) and supplies them to the Brevo request payload instead of hardcoded constants.

Changes

Email sender configuration

Layer / File(s) Summary
Configurable email sender via environment variables
.env.example, backend/api/src/services/email_service.rs
.env.example introduces EMAIL_FROM and EMAIL_FROM_NAME with defaults and documentation of the Brevo verified sender requirement. send_otp_email reads these environment variables and uses them in the Brevo request sender field, replacing hardcoded identity.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

A rabbit hops past hardcoded email,
Now vars from .env set the sender's tale,
Brevo's verified door opens wide,
No more OTP bounces—joy and pride! 🐰✉️

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: making the OTP email sender configurable via environment variables, which is the primary objective of this PR.
Linked Issues check ✅ Passed The PR fully addresses all coding requirements from issue #56: made sender email/name configurable via EMAIL_FROM and EMAIL_FROM_NAME environment variables with proper defaults, and documented the Brevo verification requirement in .env.example.
Out of Scope Changes check ✅ Passed All changes are in-scope and directly address issue #56: email_service.rs reads from env variables instead of hardcoded values, and .env.example documents the new configuration variables.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/56-email-sender

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 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 @.env.example:
- Line 10: The .env.example value for EMAIL_FROM_NAME contains whitespace and
should be quoted to avoid parsing ambiguity; update the EMAIL_FROM_NAME entry to
wrap its value in quotes (e.g., change the EMAIL_FROM_NAME line to use "txio
Team") so dotenv/shell loaders parse it consistently and preserve the space.

In `@backend/api/src/services/email_service.rs`:
- Around line 22-25: The current from_email and from_name initialization only
falls back when the env vars are missing; update their creation in
email_service.rs to treat empty or whitespace-only values as missing by trimming
and checking emptiness before using them. Replace the
std::env::var(...).unwrap_or_else(...) pattern for from_email and from_name with
logic that first obtains the env var (e.g., std::env::var(...).ok()), trims and
verifies it's not empty (or whitespace), and only then uses it; otherwise use
the existing defaults ("no-reply@txio-backend.com" and "txio Team") so
EMAIL_FROM and EMAIL_FROM_NAME set to empty strings do not become invalid
senders.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 014b79e6-fa1b-40ab-9e97-06761d257492

📥 Commits

Reviewing files that changed from the base of the PR and between a452e2f and 4362d87.

📒 Files selected for processing (2)
  • .env.example
  • backend/api/src/services/email_service.rs

Comment thread .env.example
BREVO_API_KEY=your_brevo_api_key_here
# Sender for OTP emails. Must be a verified sender/domain in your Brevo account.
EMAIL_FROM=no-reply@txio-backend.com
EMAIL_FROM_NAME=txio Team

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Quote EMAIL_FROM_NAME to avoid parser ambiguity.

txio Team contains whitespace; quoting it avoids inconsistent parsing across dotenv loaders/shell-based setups.

Suggested patch
-EMAIL_FROM_NAME=txio Team
+EMAIL_FROM_NAME="txio Team"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
EMAIL_FROM_NAME=txio Team
EMAIL_FROM_NAME="txio Team"
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 10-10: [UnorderedKey] The EMAIL_FROM_NAME key should go before the JWT_SECRET key

(UnorderedKey)


[warning] 10-10: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)

🤖 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 @.env.example at line 10, The .env.example value for EMAIL_FROM_NAME contains
whitespace and should be quoted to avoid parsing ambiguity; update the
EMAIL_FROM_NAME entry to wrap its value in quotes (e.g., change the
EMAIL_FROM_NAME line to use "txio Team") so dotenv/shell loaders parse it
consistently and preserve the space.

Source: Linters/SAST tools

Comment on lines +22 to +25
let from_email = std::env::var("EMAIL_FROM")
.unwrap_or_else(|_| "no-reply@txio-backend.com".to_string());
let from_name =
std::env::var("EMAIL_FROM_NAME").unwrap_or_else(|_| "txio Team".to_string());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle empty env values, not just missing ones.

Current fallback only runs when the variable is absent. If EMAIL_FROM/EMAIL_FROM_NAME are set to empty (or whitespace), invalid sender data is sent and OTP delivery can still fail.

Suggested patch
-        let from_email = std::env::var("EMAIL_FROM")
-            .unwrap_or_else(|_| "no-reply@txio-backend.com".to_string());
-        let from_name =
-            std::env::var("EMAIL_FROM_NAME").unwrap_or_else(|_| "txio Team".to_string());
+        let from_email = std::env::var("EMAIL_FROM")
+            .ok()
+            .map(|v| v.trim().to_string())
+            .filter(|v| !v.is_empty())
+            .unwrap_or_else(|| "no-reply@txio-backend.com".to_string());
+        let from_name = std::env::var("EMAIL_FROM_NAME")
+            .ok()
+            .map(|v| v.trim().to_string())
+            .filter(|v| !v.is_empty())
+            .unwrap_or_else(|| "txio Team".to_string());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let from_email = std::env::var("EMAIL_FROM")
.unwrap_or_else(|_| "no-reply@txio-backend.com".to_string());
let from_name =
std::env::var("EMAIL_FROM_NAME").unwrap_or_else(|_| "txio Team".to_string());
let from_email = std::env::var("EMAIL_FROM")
.ok()
.map(|v| v.trim().to_string())
.filter(|v| !v.is_empty())
.unwrap_or_else(|| "no-reply@txio-backend.com".to_string());
let from_name = std::env::var("EMAIL_FROM_NAME")
.ok()
.map(|v| v.trim().to_string())
.filter(|v| !v.is_empty())
.unwrap_or_else(|| "txio Team".to_string());
🤖 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 `@backend/api/src/services/email_service.rs` around lines 22 - 25, The current
from_email and from_name initialization only falls back when the env vars are
missing; update their creation in email_service.rs to treat empty or
whitespace-only values as missing by trimming and checking emptiness before
using them. Replace the std::env::var(...).unwrap_or_else(...) pattern for
from_email and from_name with logic that first obtains the env var (e.g.,
std::env::var(...).ok()), trims and verifies it's not empty (or whitespace), and
only then uses it; otherwise use the existing defaults
("no-reply@txio-backend.com" and "txio Team") so EMAIL_FROM and EMAIL_FROM_NAME
set to empty strings do not become invalid senders.

@Kingvic300 Kingvic300 merged commit bddfc02 into main Jun 7, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Medium] OTP emails use a hardcoded, likely-unverified sender domain

1 participant