766 lines
18 KiB
Markdown
766 lines
18 KiB
Markdown
|
|
# Email System
|
||
|
|
|
||
|
|
<div align="center">
|
||
|
|
<img src="../../../logos/rustelo_dev-logo-h.svg" alt="RUSTELO" width="300" />
|
||
|
|
</div>
|
||
|
|
|
||
|
|
This guide covers RUSTELO's comprehensive email system, including setup, configuration, usage, and best practices for integrating email functionality into your application.
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
The RUSTELO email system provides a complete solution for sending emails from your web application with support for multiple providers, template-based emails, form submissions, and both server-side and client-side integration.
|
||
|
|
|
||
|
|
### Architecture
|
||
|
|
|
||
|
|
- **Email Service**: Core service that handles email sending
|
||
|
|
- **Providers**: Pluggable email providers (SMTP, SendGrid, Console)
|
||
|
|
- **Templates**: Handlebars-based email templates
|
||
|
|
- **Forms**: Ready-to-use contact and support form components
|
||
|
|
- **API**: REST endpoints for email operations
|
||
|
|
|
||
|
|
## Features
|
||
|
|
|
||
|
|
✨ **Multiple Providers**
|
||
|
|
- SMTP (Gmail, Outlook, custom servers)
|
||
|
|
- SendGrid API
|
||
|
|
- Console output (development)
|
||
|
|
|
||
|
|
📧 **Template System**
|
||
|
|
- Handlebars templates
|
||
|
|
- HTML and text versions
|
||
|
|
- Custom helpers
|
||
|
|
- Variable substitution
|
||
|
|
|
||
|
|
🔧 **Form Integration**
|
||
|
|
- Contact forms
|
||
|
|
- Support forms with priorities
|
||
|
|
- Custom form handling
|
||
|
|
|
||
|
|
🛡️ **Security**
|
||
|
|
- Input validation
|
||
|
|
- Rate limiting
|
||
|
|
- CSRF protection
|
||
|
|
- Secure configuration
|
||
|
|
|
||
|
|
🎨 **Rich Components**
|
||
|
|
- React/Leptos form components
|
||
|
|
- Real-time validation
|
||
|
|
- Error handling
|
||
|
|
- Success feedback
|
||
|
|
|
||
|
|
## Quick Start
|
||
|
|
|
||
|
|
### 1. Enable Email Feature
|
||
|
|
|
||
|
|
Make sure the email feature is enabled in your `Cargo.toml`:
|
||
|
|
|
||
|
|
```toml
|
||
|
|
[features]
|
||
|
|
default = ["email"]
|
||
|
|
email = ["lettre", "handlebars", "urlencoding"]
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Basic Configuration
|
||
|
|
|
||
|
|
Add email configuration to your `config.toml`:
|
||
|
|
|
||
|
|
```toml
|
||
|
|
[email]
|
||
|
|
enabled = true
|
||
|
|
provider = "console" # Start with console for development
|
||
|
|
from_email = "noreply@yourapp.com"
|
||
|
|
from_name = "Your App"
|
||
|
|
template_dir = "templates/email"
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Create Template Directory
|
||
|
|
|
||
|
|
```bash
|
||
|
|
mkdir -p templates/email/html
|
||
|
|
mkdir -p templates/email/text
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. Start Using
|
||
|
|
|
||
|
|
```rust
|
||
|
|
// Send a simple email
|
||
|
|
let result = email_service.send_simple_email(
|
||
|
|
"user@example.com",
|
||
|
|
"Welcome!",
|
||
|
|
"Thank you for signing up!"
|
||
|
|
).await?;
|
||
|
|
|
||
|
|
// Send a contact form
|
||
|
|
let result = email_service.send_contact_form(
|
||
|
|
"John Doe",
|
||
|
|
"john@example.com",
|
||
|
|
"Question about pricing",
|
||
|
|
"I'd like to know more about your pricing plans.",
|
||
|
|
"admin@yourapp.com"
|
||
|
|
).await?;
|
||
|
|
```
|
||
|
|
|
||
|
|
## Configuration
|
||
|
|
|
||
|
|
### Email Configuration Options
|
||
|
|
|
||
|
|
```toml
|
||
|
|
[email]
|
||
|
|
# Basic settings
|
||
|
|
enabled = true
|
||
|
|
provider = "smtp" # "smtp", "sendgrid", "console"
|
||
|
|
from_email = "noreply@yourapp.com"
|
||
|
|
from_name = "Your App Name"
|
||
|
|
template_dir = "templates/email"
|
||
|
|
|
||
|
|
# SMTP settings (when provider = "smtp")
|
||
|
|
smtp_host = "smtp.gmail.com"
|
||
|
|
smtp_port = 587
|
||
|
|
smtp_username = "your-email@gmail.com"
|
||
|
|
smtp_password = "@encrypted_smtp_password"
|
||
|
|
smtp_use_tls = false
|
||
|
|
smtp_use_starttls = true
|
||
|
|
|
||
|
|
# SendGrid settings (when provider = "sendgrid")
|
||
|
|
sendgrid_api_key = "@encrypted_sendgrid_api_key"
|
||
|
|
sendgrid_endpoint = "https://api.sendgrid.com/v3/mail/send"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Environment-Specific Configuration
|
||
|
|
|
||
|
|
```toml
|
||
|
|
# Development
|
||
|
|
[environments.development]
|
||
|
|
email.provider = "console"
|
||
|
|
email.enabled = true
|
||
|
|
|
||
|
|
# Production
|
||
|
|
[environments.production]
|
||
|
|
email.provider = "sendgrid"
|
||
|
|
email.sendgrid_api_key = "@encrypted_sendgrid_api_key"
|
||
|
|
email.enabled = true
|
||
|
|
```
|
||
|
|
|
||
|
|
## Email Providers
|
||
|
|
|
||
|
|
### Console Provider
|
||
|
|
|
||
|
|
Perfect for development and testing. Prints emails to the console.
|
||
|
|
|
||
|
|
```toml
|
||
|
|
[email]
|
||
|
|
provider = "console"
|
||
|
|
```
|
||
|
|
|
||
|
|
**Features:**
|
||
|
|
- No external dependencies
|
||
|
|
- Immediate feedback
|
||
|
|
- Safe for development
|
||
|
|
|
||
|
|
### SMTP Provider
|
||
|
|
|
||
|
|
Use any SMTP server including Gmail, Outlook, or custom servers.
|
||
|
|
|
||
|
|
```toml
|
||
|
|
[email]
|
||
|
|
provider = "smtp"
|
||
|
|
smtp_host = "smtp.gmail.com"
|
||
|
|
smtp_port = 587
|
||
|
|
smtp_username = "your-email@gmail.com"
|
||
|
|
smtp_password = "@encrypted_smtp_password"
|
||
|
|
smtp_use_starttls = true
|
||
|
|
```
|
||
|
|
|
||
|
|
**Common SMTP Configurations:**
|
||
|
|
|
||
|
|
**Gmail:**
|
||
|
|
```toml
|
||
|
|
smtp_host = "smtp.gmail.com"
|
||
|
|
smtp_port = 587
|
||
|
|
smtp_use_starttls = true
|
||
|
|
# Requires App Password (not regular password)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Outlook:**
|
||
|
|
```toml
|
||
|
|
smtp_host = "smtp-mail.outlook.com"
|
||
|
|
smtp_port = 587
|
||
|
|
smtp_use_starttls = true
|
||
|
|
```
|
||
|
|
|
||
|
|
**Custom SMTP:**
|
||
|
|
```toml
|
||
|
|
smtp_host = "mail.yourserver.com"
|
||
|
|
smtp_port = 587
|
||
|
|
smtp_use_starttls = true
|
||
|
|
```
|
||
|
|
|
||
|
|
### SendGrid Provider
|
||
|
|
|
||
|
|
Use SendGrid's API for reliable email delivery.
|
||
|
|
|
||
|
|
```toml
|
||
|
|
[email]
|
||
|
|
provider = "sendgrid"
|
||
|
|
sendgrid_api_key = "@encrypted_sendgrid_api_key"
|
||
|
|
```
|
||
|
|
|
||
|
|
**Features:**
|
||
|
|
- High deliverability
|
||
|
|
- Built-in analytics
|
||
|
|
- Bounce handling
|
||
|
|
- Reliable service
|
||
|
|
|
||
|
|
## Templates
|
||
|
|
|
||
|
|
### Template Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
templates/email/
|
||
|
|
├── html/
|
||
|
|
│ ├── contact.hbs
|
||
|
|
│ ├── support.hbs
|
||
|
|
│ ├── welcome.hbs
|
||
|
|
│ └── notification.hbs
|
||
|
|
└── text/
|
||
|
|
├── contact.hbs
|
||
|
|
├── support.hbs
|
||
|
|
├── welcome.hbs
|
||
|
|
└── notification.hbs
|
||
|
|
```
|
||
|
|
|
||
|
|
### Template Naming
|
||
|
|
|
||
|
|
- `contact.hbs` - Contact form emails
|
||
|
|
- `support.hbs` - Support form emails
|
||
|
|
- `welcome.hbs` - Welcome/registration emails
|
||
|
|
- `notification.hbs` - General notifications
|
||
|
|
|
||
|
|
### Handlebars Helpers
|
||
|
|
|
||
|
|
Built-in helpers for common email tasks:
|
||
|
|
|
||
|
|
- `{{format_date}}` - Format dates
|
||
|
|
- `{{format_currency}}` - Format money
|
||
|
|
- `{{upper}}` - Uppercase text
|
||
|
|
- `{{lower}}` - Lowercase text
|
||
|
|
- `{{capitalize}}` - Capitalize words
|
||
|
|
|
||
|
|
### Example Template
|
||
|
|
|
||
|
|
**templates/email/html/contact.hbs:**
|
||
|
|
|
||
|
|
```html
|
||
|
|
<!DOCTYPE html>
|
||
|
|
<html>
|
||
|
|
<head>
|
||
|
|
<meta charset="utf-8">
|
||
|
|
<title>Contact Form Submission</title>
|
||
|
|
<style>
|
||
|
|
body { font-family: Arial, sans-serif; margin: 20px; }
|
||
|
|
.header { background-color: #f8f9fa; padding: 20px; border-radius: 8px; }
|
||
|
|
.content { margin: 20px 0; }
|
||
|
|
.footer { color: #666; font-size: 12px; }
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<div class="header">
|
||
|
|
<h1>New Contact Form Submission</h1>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="content">
|
||
|
|
<p><strong>Name:</strong> {{name}}</p>
|
||
|
|
<p><strong>Email:</strong> {{email}}</p>
|
||
|
|
<p><strong>Subject:</strong> {{subject}}</p>
|
||
|
|
<p><strong>Message:</strong></p>
|
||
|
|
<p>{{message}}</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="footer">
|
||
|
|
<p>Sent at {{format_date timestamp}}</p>
|
||
|
|
</div>
|
||
|
|
</body>
|
||
|
|
</html>
|
||
|
|
```
|
||
|
|
|
||
|
|
**templates/email/text/contact.hbs:**
|
||
|
|
|
||
|
|
```text
|
||
|
|
New Contact Form Submission
|
||
|
|
|
||
|
|
Name: {{name}}
|
||
|
|
Email: {{email}}
|
||
|
|
Subject: {{subject}}
|
||
|
|
|
||
|
|
Message:
|
||
|
|
{{message}}
|
||
|
|
|
||
|
|
Sent at {{format_date timestamp}}
|
||
|
|
```
|
||
|
|
|
||
|
|
## API Endpoints
|
||
|
|
|
||
|
|
### GET /api/email/status
|
||
|
|
|
||
|
|
Check email system status.
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"enabled": true,
|
||
|
|
"provider": "smtp",
|
||
|
|
"configured": true,
|
||
|
|
"templates": ["contact", "support", "welcome", "notification"]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### POST /api/email/contact
|
||
|
|
|
||
|
|
Send a contact form email.
|
||
|
|
|
||
|
|
**Request:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"name": "John Doe",
|
||
|
|
"email": "john@example.com",
|
||
|
|
"subject": "Question about pricing",
|
||
|
|
"message": "I'd like to know more about your pricing plans.",
|
||
|
|
"recipient": "admin@yourapp.com"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"message": "Email sent successfully",
|
||
|
|
"message_id": "abc123def456",
|
||
|
|
"status": "sent"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### POST /api/email/support
|
||
|
|
|
||
|
|
Send a support form email with priority.
|
||
|
|
|
||
|
|
**Request:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"name": "Jane Smith",
|
||
|
|
"email": "jane@example.com",
|
||
|
|
"subject": "Technical Issue",
|
||
|
|
"message": "Having trouble with login functionality.",
|
||
|
|
"priority": "high",
|
||
|
|
"category": "technical",
|
||
|
|
"recipient": "support@yourapp.com"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### POST /api/email/send
|
||
|
|
|
||
|
|
Send a template-based email.
|
||
|
|
|
||
|
|
**Request:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"to": "user@example.com",
|
||
|
|
"subject": "Welcome to Our Platform",
|
||
|
|
"template": "welcome",
|
||
|
|
"template_data": {
|
||
|
|
"user_name": "John Doe",
|
||
|
|
"activation_link": "https://yourapp.com/activate/token123"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### POST /api/email/notification
|
||
|
|
|
||
|
|
Send a notification email.
|
||
|
|
|
||
|
|
**Request:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"to": "user@example.com",
|
||
|
|
"title": "Important Update",
|
||
|
|
"message": "Your account has been updated successfully.",
|
||
|
|
"content": "Additional details about the update..."
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Client Components
|
||
|
|
|
||
|
|
### ContactForm Component
|
||
|
|
|
||
|
|
```rust
|
||
|
|
#[component]
|
||
|
|
pub fn ContactForm() -> impl IntoView {
|
||
|
|
let (form_data, set_form_data) = create_signal(ContactFormData::default());
|
||
|
|
let (is_submitting, set_is_submitting) = create_signal(false);
|
||
|
|
let (message, set_message) = create_signal(String::new());
|
||
|
|
|
||
|
|
let submit_form = create_action(move |data: &ContactFormData| {
|
||
|
|
let data = data.clone();
|
||
|
|
async move {
|
||
|
|
set_is_submitting(true);
|
||
|
|
let result = send_contact_form(data).await;
|
||
|
|
set_is_submitting(false);
|
||
|
|
match result {
|
||
|
|
Ok(_) => set_message("Thank you for your message! We'll get back to you soon.".to_string()),
|
||
|
|
Err(e) => set_message(format!("Error sending message: {}", e)),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
view! {
|
||
|
|
<div class="contact-form">
|
||
|
|
<h2>"Contact Us"</h2>
|
||
|
|
<form on:submit=move |ev| {
|
||
|
|
ev.prevent_default();
|
||
|
|
submit_form.dispatch(form_data.get());
|
||
|
|
}>
|
||
|
|
<div class="form-group">
|
||
|
|
<label for="name">"Name"</label>
|
||
|
|
<input
|
||
|
|
type="text"
|
||
|
|
id="name"
|
||
|
|
required
|
||
|
|
on:input=move |ev| {
|
||
|
|
set_form_data.update(|data| data.name = event_target_value(&ev));
|
||
|
|
}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="form-group">
|
||
|
|
<label for="email">"Email"</label>
|
||
|
|
<input
|
||
|
|
type="email"
|
||
|
|
id="email"
|
||
|
|
required
|
||
|
|
on:input=move |ev| {
|
||
|
|
set_form_data.update(|data| data.email = event_target_value(&ev));
|
||
|
|
}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="form-group">
|
||
|
|
<label for="subject">"Subject"</label>
|
||
|
|
<input
|
||
|
|
type="text"
|
||
|
|
id="subject"
|
||
|
|
required
|
||
|
|
on:input=move |ev| {
|
||
|
|
set_form_data.update(|data| data.subject = event_target_value(&ev));
|
||
|
|
}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="form-group">
|
||
|
|
<label for="message">"Message"</label>
|
||
|
|
<textarea
|
||
|
|
id="message"
|
||
|
|
required
|
||
|
|
on:input=move |ev| {
|
||
|
|
set_form_data.update(|data| data.message = event_target_value(&ev));
|
||
|
|
}
|
||
|
|
></textarea>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<button type="submit" disabled=move || is_submitting.get()>
|
||
|
|
{move || if is_submitting.get() { "Sending..." } else { "Send Message" }}
|
||
|
|
</button>
|
||
|
|
</form>
|
||
|
|
|
||
|
|
<Show when=move || !message.get().is_empty()>
|
||
|
|
<div class="message">{move || message.get()}</div>
|
||
|
|
</Show>
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### SupportForm Component
|
||
|
|
|
||
|
|
```rust
|
||
|
|
#[component]
|
||
|
|
pub fn SupportForm() -> impl IntoView {
|
||
|
|
let (form_data, set_form_data) = create_signal(SupportFormData::default());
|
||
|
|
let (is_submitting, set_is_submitting) = create_signal(false);
|
||
|
|
let (message, set_message) = create_signal(String::new());
|
||
|
|
|
||
|
|
let submit_form = create_action(move |data: &SupportFormData| {
|
||
|
|
let data = data.clone();
|
||
|
|
async move {
|
||
|
|
set_is_submitting(true);
|
||
|
|
let result = send_support_form(data).await;
|
||
|
|
set_is_submitting(false);
|
||
|
|
match result {
|
||
|
|
Ok(_) => set_message("Support ticket submitted successfully!".to_string()),
|
||
|
|
Err(e) => set_message(format!("Error submitting ticket: {}", e)),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
view! {
|
||
|
|
<div class="support-form">
|
||
|
|
<h2>"Support Request"</h2>
|
||
|
|
<form on:submit=move |ev| {
|
||
|
|
ev.prevent_default();
|
||
|
|
submit_form.dispatch(form_data.get());
|
||
|
|
}>
|
||
|
|
// Similar form fields with priority selector
|
||
|
|
<div class="form-group">
|
||
|
|
<label for="priority">"Priority"</label>
|
||
|
|
<select
|
||
|
|
id="priority"
|
||
|
|
on:change=move |ev| {
|
||
|
|
set_form_data.update(|data| data.priority = event_target_value(&ev));
|
||
|
|
}
|
||
|
|
>
|
||
|
|
<option value="low">"Low"</option>
|
||
|
|
<option value="medium">"Medium"</option>
|
||
|
|
<option value="high">"High"</option>
|
||
|
|
<option value="urgent">"Urgent"</option>
|
||
|
|
</select>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<button type="submit" disabled=move || is_submitting.get()>
|
||
|
|
{move || if is_submitting.get() { "Submitting..." } else { "Submit Ticket" }}
|
||
|
|
</button>
|
||
|
|
</form>
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Server Usage
|
||
|
|
|
||
|
|
### Basic Email Sending
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use server::email::{EmailService, EmailConfig};
|
||
|
|
|
||
|
|
#[tokio::main]
|
||
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||
|
|
let config = EmailConfig::from_file("config.toml")?;
|
||
|
|
let email_service = EmailService::new(config).await?;
|
||
|
|
|
||
|
|
// Send simple email
|
||
|
|
email_service.send_simple_email(
|
||
|
|
"user@example.com",
|
||
|
|
"Welcome!",
|
||
|
|
"Thank you for signing up!"
|
||
|
|
).await?;
|
||
|
|
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Template-Based Emails
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use std::collections::HashMap;
|
||
|
|
|
||
|
|
#[server(SendWelcomeEmail, "/api/email/welcome")]
|
||
|
|
pub async fn send_welcome_email(
|
||
|
|
email: String,
|
||
|
|
name: String,
|
||
|
|
activation_token: String,
|
||
|
|
) -> Result<String, ServerFnError> {
|
||
|
|
let email_service = get_email_service().await?;
|
||
|
|
|
||
|
|
let mut template_data = HashMap::new();
|
||
|
|
template_data.insert("user_name".to_string(), name);
|
||
|
|
template_data.insert("activation_link".to_string(),
|
||
|
|
format!("https://yourapp.com/activate/{}", activation_token));
|
||
|
|
|
||
|
|
email_service.send_template_email(
|
||
|
|
&email,
|
||
|
|
"Welcome to Our Platform",
|
||
|
|
"welcome",
|
||
|
|
template_data
|
||
|
|
).await?;
|
||
|
|
|
||
|
|
Ok("Welcome email sent successfully".to_string())
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Form Handling
|
||
|
|
|
||
|
|
```rust
|
||
|
|
#[server(HandleContactForm, "/api/email/contact")]
|
||
|
|
pub async fn handle_contact_form(
|
||
|
|
name: String,
|
||
|
|
email: String,
|
||
|
|
subject: String,
|
||
|
|
message: String,
|
||
|
|
) -> Result<String, ServerFnError> {
|
||
|
|
let email_service = get_email_service().await?;
|
||
|
|
|
||
|
|
// Validate input
|
||
|
|
if name.is_empty() || email.is_empty() || message.is_empty() {
|
||
|
|
return Err(ServerFnError::ServerError("All fields are required".to_string()));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Send email
|
||
|
|
email_service.send_contact_form(
|
||
|
|
&name,
|
||
|
|
&email,
|
||
|
|
&subject,
|
||
|
|
&message,
|
||
|
|
"admin@yourapp.com"
|
||
|
|
).await?;
|
||
|
|
|
||
|
|
Ok("Contact form submitted successfully".to_string())
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Environment Variables
|
||
|
|
|
||
|
|
### Common Variables
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# SMTP Configuration
|
||
|
|
SMTP_HOST=smtp.gmail.com
|
||
|
|
SMTP_PORT=587
|
||
|
|
SMTP_USERNAME=your-email@gmail.com
|
||
|
|
SMTP_PASSWORD=your-app-password
|
||
|
|
SMTP_USE_STARTTLS=true
|
||
|
|
|
||
|
|
# SendGrid Configuration
|
||
|
|
SENDGRID_API_KEY=your-sendgrid-api-key
|
||
|
|
|
||
|
|
# Email Settings
|
||
|
|
EMAIL_FROM=noreply@yourapp.com
|
||
|
|
EMAIL_FROM_NAME="Your App"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Using in Configuration
|
||
|
|
|
||
|
|
```toml
|
||
|
|
[email]
|
||
|
|
smtp_username = "${SMTP_USERNAME}"
|
||
|
|
smtp_password = "@encrypted_smtp_password"
|
||
|
|
sendgrid_api_key = "@encrypted_sendgrid_api_key"
|
||
|
|
from_email = "${EMAIL_FROM}"
|
||
|
|
```
|
||
|
|
|
||
|
|
## Security Considerations
|
||
|
|
|
||
|
|
### 1. Credential Management
|
||
|
|
|
||
|
|
Always encrypt sensitive credentials:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Encrypt SMTP password
|
||
|
|
cargo run --bin config_crypto_tool encrypt "your-smtp-password"
|
||
|
|
|
||
|
|
# Encrypt SendGrid API key
|
||
|
|
cargo run --bin config_crypto_tool encrypt "your-sendgrid-api-key"
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Input Validation
|
||
|
|
|
||
|
|
Always validate email inputs:
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use validator::{Validate, ValidationError};
|
||
|
|
|
||
|
|
#[derive(Validate)]
|
||
|
|
struct ContactFormData {
|
||
|
|
#[validate(length(min = 1, message = "Name is required"))]
|
||
|
|
name: String,
|
||
|
|
|
||
|
|
#[validate(email(message = "Invalid email address"))]
|
||
|
|
email: String,
|
||
|
|
|
||
|
|
#[validate(length(min = 1, max = 1000, message = "Message must be between 1 and 1000 characters"))]
|
||
|
|
message: String,
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Rate Limiting
|
||
|
|
|
||
|
|
Implement rate limiting for email endpoints:
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use tower_governor::{governor::GovernorLayer, GovernorConfigBuilder};
|
||
|
|
|
||
|
|
// Limit to 5 emails per minute per IP
|
||
|
|
let governor_config = GovernorConfigBuilder::default()
|
||
|
|
.per_minute(5)
|
||
|
|
.burst_size(2)
|
||
|
|
.finish()
|
||
|
|
.unwrap();
|
||
|
|
|
||
|
|
let governor_layer = GovernorLayer {
|
||
|
|
config: Arc::new(governor_config),
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. CSRF Protection
|
||
|
|
|
||
|
|
Enable CSRF protection for email forms:
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use axum_csrf::{CsrfConfig, CsrfLayer};
|
||
|
|
|
||
|
|
let csrf_config = CsrfConfig::default();
|
||
|
|
let csrf_layer = CsrfLayer::new(csrf_config);
|
||
|
|
```
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
### Common Issues
|
||
|
|
|
||
|
|
**Email not sending:**
|
||
|
|
- Check provider configuration
|
||
|
|
- Verify credentials
|
||
|
|
- Check network connectivity
|
||
|
|
- Review email service logs
|
||
|
|
|
||
|
|
**Template not found:**
|
||
|
|
- Verify template directory path
|
||
|
|
- Check template file naming
|
||
|
|
- Ensure HTML and text versions exist
|
||
|
|
|
||
|
|
**Authentication failed:**
|
||
|
|
- For Gmail: Use App Password, not regular password
|
||
|
|
- For other providers: Check username/password
|
||
|
|
- Verify server and port settings
|
||
|
|
|
||
|
|
**Rate limiting:**
|
||
|
|
- Check provider limits
|
||
|
|
- Implement proper rate limiting
|
||
|
|
- Consider using queues for bulk emails
|
||
|
|
|
||
|
|
### Debug Mode
|
||
|
|
|
||
|
|
Enable debug logging to troubleshoot issues:
|
||
|
|
|
||
|
|
```toml
|
||
|
|
[logging]
|
||
|
|
level = "debug"
|
||
|
|
|
||
|
|
[email]
|
||
|
|
enabled = true
|
||
|
|
debug = true
|
||
|
|
```
|
||
|
|
|
||
|
|
## Best Practices
|
||
|
|
|
||
|
|
1. **Use encrypted configuration** for sensitive credentials
|
||
|
|
2. **Implement proper validation** for all email inputs
|
||
|
|
3. **Use rate limiting** to prevent abuse
|
||
|
|
4. **Provide both HTML and text** versions of templates
|
||
|
|
5. **Test with console provider** during development
|
||
|
|
6. **Monitor email delivery** in production
|
||
|
|
7. **Handle errors gracefully** with user-friendly messages
|
||
|
|
8. **Use meaningful subject lines** and sender names
|
||
|
|
|
||
|
|
## Next Steps
|
||
|
|
|
||
|
|
- [API Reference](../../api/email.md)
|
||
|
|
- [Security Best Practices](../../security/best-practices.md)
|
||
|
|
- [Configuration Guide](../../configuration/files.md)
|
||
|
|
- [Deployment Guide](../../deployment/production.md)
|
||
|
|
|
||
|
|
The email system provides a robust foundation for all your application's communication needs while maintaining security and reliability.
|