diff --git a/services/notification/README.md b/services/notification/README.md index 1447825d..9176b8d8 100644 --- a/services/notification/README.md +++ b/services/notification/README.md @@ -868,10 +868,11 @@ email_open_rate = Gauge( **Email (SMTP) Configuration:** - `SMTP_HOST` - SMTP server host - `SMTP_PORT` - SMTP server port (default: 587) -- `SMTP_USERNAME` - SMTP username -- `SMTP_PASSWORD` - SMTP password +- `SMTP_USERNAME` - SMTP username (optional for trusted relay) +- `SMTP_PASSWORD` - SMTP password (optional for trusted relay) - `SMTP_FROM_ADDRESS` - From email address - `SMTP_USE_TLS` - Enable TLS (default: true) +- `SMTP_REQUIRE_AUTH` - Require SMTP authentication (default: false for trusted relay) **Twilio Configuration:** - `TWILIO_ACCOUNT_SID` - Twilio account SID @@ -890,6 +891,86 @@ email_open_rate = Gauge( - `SMS_COST_PER_MESSAGE` - Cost per SMS (default: 0.08 EUR) - `EMAIL_COST_PER_MESSAGE` - Cost per email (default: 0.001 EUR) +## Mailu Integration + +The notification service is designed to work seamlessly with Mailu email servers, especially for internal bakery communications. + +### Internal Email Delivery (Recommended) +When sending emails to addresses within the same Mailu domain (e.g., `user@your-bakery.com`), Mailu handles these as internal messages: + +- **No SMTP authentication required** - Mailu trusts internal subnet connections +- **Direct inbox delivery** - Messages stay within Mailu's internal mail store +- **Faster delivery** - No external SMTP relay needed +- **Better reliability** - No dependency on external email providers + +### Configuration for Mailu + +```bash +# Mailu internal configuration (recommended for bakery communications) +export SMTP_HOST=mailu.your-domain.com # or mailu.internal if using internal DNS +export SMTP_PORT=587 # Standard submission port +export SMTP_REQUIRE_AUTH=false # No authentication needed for internal relay +export DEFAULT_FROM_EMAIL=noreply@your-bakery.com + +# Optional: If using Mailu's webmail interface +export SMTP_PORT=25 # Alternative: use port 25 for internal relay +``` + +### Mailu Subnet Trust Configuration + +For Mailu to accept unauthenticated connections from the notification service: + +1. **Configure Mailu's relay networks** in `mailu.env`: + ```ini + # Add your Kubernetes service subnet to trusted networks + RELAYNETS=10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 + ``` + +2. **Ensure DNS resolution** works between services: + ```bash + # From notification service pod, test Mailu connectivity + nslookup mailu.your-domain.com + telnet mailu.your-domain.com 587 + ``` + +3. **Verify internal delivery** with Mailu logs: + ```bash + # Check Mailu container logs for internal message processing + kubectl logs -f mailu-pod-name | grep "internal delivery" + ``` + +## Trusted Relay Configuration + +The notification service supports both authenticated SMTP and trusted relay modes: + +### Trusted Relay Mode (Default) +For internal Mailu servers or other trusted SMTP relays that don't require authentication: + +```bash +# No authentication - trusted relay mode (default) +export SMTP_HOST=mailu.example.com +export SMTP_PORT=587 +export SMTP_REQUIRE_AUTH=false # Explicitly disable auth (default is false) +# SMTP_USERNAME and SMTP_PASSWORD can be empty +``` + +### Authenticated SMTP Mode +For external SMTP services like SendGrid, Amazon SES, or Gmail: + +```bash +# With authentication - external SMTP services +export SMTP_HOST=smtp.sendgrid.net +export SMTP_PORT=587 +export SMTP_USERNAME=apikey +export SMTP_PASSWORD=your_sendgrid_api_key +export SMTP_REQUIRE_AUTH=true # Explicitly enable auth +``` + +The service automatically detects the configuration: +- If `SMTP_REQUIRE_AUTH=false` (default), authentication is skipped +- If `SMTP_REQUIRE_AUTH=true` but credentials are missing, it will fail with clear error +- If credentials are provided but `SMTP_REQUIRE_AUTH=false`, it will still skip authentication + ## Development Setup ### Prerequisites @@ -901,6 +982,8 @@ email_open_rate = Gauge( - Twilio account (for WhatsApp/SMS) ### Local Development + +#### With Mailu (Recommended for Bakery-IA) ```bash cd services/notification python -m venv venv @@ -911,9 +994,43 @@ pip install -r requirements.txt export DATABASE_URL=postgresql://user:pass@localhost:5432/notification export REDIS_URL=redis://localhost:6379/0 export RABBITMQ_URL=amqp://guest:guest@localhost:5672/ + +# Mailu configuration (internal bakery communications) +export SMTP_HOST=mailu.bakery-ia.local # Internal Mailu service +export SMTP_PORT=587 # Submission port +export SMTP_REQUIRE_AUTH=false # No auth needed - Mailu subnet trust +export DEFAULT_FROM_EMAIL=noreply@bakeryforecast.es + +# Optional: For testing external emails +export ENABLE_EMAIL_NOTIFICATIONS=true + +export TWILIO_ACCOUNT_SID=your_twilio_sid +export TWILIO_AUTH_TOKEN=your_twilio_token + +alembic upgrade head +python main.py +``` + +#### With External SMTP (SendGrid, Gmail, etc.) +```bash +cd services/notification +python -m venv venv +source venv/bin/activate + +pip install -r requirements.txt + +export DATABASE_URL=postgresql://user:pass@localhost:5432/notification +export REDIS_URL=redis://localhost:6379/0 +export RABBITMQ_URL=amqp://guest:guest@localhost:5672/ + +# External SMTP configuration (SendGrid example) export SMTP_HOST=smtp.sendgrid.net +export SMTP_PORT=587 export SMTP_USERNAME=apikey export SMTP_PASSWORD=your_sendgrid_api_key +export SMTP_REQUIRE_AUTH=true # Explicit authentication required +export DEFAULT_FROM_EMAIL=noreply@yourdomain.com + export TWILIO_ACCOUNT_SID=your_twilio_sid export TWILIO_AUTH_TOKEN=your_twilio_token diff --git a/services/notification/app/core/config.py b/services/notification/app/core/config.py index c2e1a685..fa711759 100644 --- a/services/notification/app/core/config.py +++ b/services/notification/app/core/config.py @@ -47,6 +47,7 @@ class NotificationSettings(BaseServiceSettings): SMTP_PASSWORD: str = os.getenv("SMTP_PASSWORD", "") SMTP_TLS: bool = os.getenv("SMTP_TLS", "true").lower() == "true" SMTP_SSL: bool = os.getenv("SMTP_SSL", "false").lower() == "true" + SMTP_REQUIRE_AUTH: bool = os.getenv("SMTP_REQUIRE_AUTH", "false").lower() == "true" # Email Settings DEFAULT_FROM_EMAIL: str = os.getenv("DEFAULT_FROM_EMAIL", "noreply@bakeryforecast.es") diff --git a/services/notification/app/services/email_service.py b/services/notification/app/services/email_service.py index 3c297ed6..f2c1a0b6 100644 --- a/services/notification/app/services/email_service.py +++ b/services/notification/app/services/email_service.py @@ -37,8 +37,15 @@ class EmailService: self.smtp_password = settings.SMTP_PASSWORD self.smtp_tls = settings.SMTP_TLS self.smtp_ssl = settings.SMTP_SSL + self.smtp_require_auth = settings.SMTP_REQUIRE_AUTH self.default_from_email = settings.DEFAULT_FROM_EMAIL self.default_from_name = settings.DEFAULT_FROM_NAME + + # Log SMTP configuration mode + if not self.smtp_require_auth or (not self.smtp_user and not self.smtp_password): + logger.info("Email service configured for trusted relay mode (no authentication)") + else: + logger.info("Email service configured with SMTP authentication") async def send_email( self, @@ -287,9 +294,12 @@ class EmailService: else: raise starttls_error - # Login only if credentials are provided (optional for trusted relay) - if self.smtp_user and self.smtp_password: + # Login only if required by configuration or if credentials are provided + # This allows for trusted relay mode where authentication is not needed + if self.smtp_require_auth and self.smtp_user and self.smtp_password: await server.login(self.smtp_user, self.smtp_password) + elif not self.smtp_require_auth: + logger.debug("Skipping SMTP authentication - trusted relay mode") await server.quit() logger.info("Email service health check passed") @@ -327,9 +337,12 @@ class EmailService: if self.smtp_tls and not self.smtp_ssl: await server.starttls() - # Login only if credentials are provided (optional for trusted relay) - if self.smtp_user and self.smtp_password: + # Login only if required by configuration or if credentials are provided + # This allows for trusted relay mode where authentication is not needed + if self.smtp_require_auth and self.smtp_user and self.smtp_password: await server.login(self.smtp_user, self.smtp_password) + elif not self.smtp_require_auth: + logger.debug("Skipping SMTP authentication - trusted relay mode") # Send email await server.send_message(message, from_addr=from_email, to_addrs=[to_email])