-
Notifications
You must be signed in to change notification settings - Fork 0
feat(backend): support smtp #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e11c885
7cd2a69
c82e676
dff13b2
394182c
e355257
922adb1
f3f7cc2
833e21f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,8 @@ | ||
| database_url=postgresql://username:password@host:port/db_name | ||
| redis_url=redis://127.0.0.1:6379 | ||
| sentry_dsn= | ||
| smtp_host=smtp.example.com | ||
| smtp_port=587 | ||
| smtp_username=your_smtp_username | ||
| smtp_password=your_smtp_password | ||
| smtp_from=no-reply@example.com |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,18 @@ | ||
| use config::{Config, Environment}; | ||
| use serde::Deserialize; | ||
|
|
||
| #[derive(Clone, Debug, Deserialize)] | ||
| #[derive(Clone, Deserialize)] | ||
| pub struct Settings { | ||
| pub database_url: String, | ||
| pub redis_url: String, | ||
| pub sentry_dsn: Option<String>, | ||
| #[serde(default = "default_allow_origin")] | ||
| pub allow_origin: String, | ||
| pub smtp_host: String, | ||
| pub smtp_port: u16, | ||
| pub smtp_username: String, | ||
| pub smtp_password: String, | ||
| pub smtp_from: String, | ||
|
Comment on lines
+11
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SMTP設定を全必須にすると既存環境で起動不能になる可能性があります。 Line 11-15 の As per coding guidelines 「Rust / Axum / SeaORM のバックエンドです。認証・認可、テナント境界、SQL の正しさ、エラーハンドリング、非同期処理の安全性を優先して確認してください。」。 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| fn default_allow_origin() -> String { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| pub mod auth; | ||
| pub mod redis; | ||
| pub mod smtp; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| //! SMTPクライアントを提供するモジュール | ||
|
|
||
| use lettre::Tokio1Executor; | ||
| use lettre::message::{Mailbox, Message, MultiPart, SinglePart}; | ||
| use lettre::{AsyncSmtpTransport, AsyncTransport, transport::smtp::authentication::Credentials}; | ||
| use std::time::Duration; | ||
| use tokio::time::timeout; | ||
|
|
||
| /// SMTP送信タイムアウト(秒) | ||
| const SMTP_SEND_TIMEOUT_SECS: u64 = 30; | ||
|
|
||
| /// SMTPクライアントの構造体 | ||
| #[derive(Clone)] | ||
| pub struct SmtpClient { | ||
| mailer: AsyncSmtpTransport<Tokio1Executor>, | ||
| } | ||
|
|
||
| /// SmtpClientの実装 | ||
| impl SmtpClient { | ||
| /// 新しいSMTPクライアントを作成する関数 | ||
| /// | ||
| /// # Arguments | ||
| /// * `smtp_server` - SMTPサーバーのアドレス | ||
| /// * `smtp_port` - SMTPサーバーのポート番号 | ||
| /// * `username` - SMTPサーバーの認証に使用するユーザー名 | ||
| /// * `password` - SMTPサーバーの認証に使用するパスワード | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ```no_run | ||
| /// use backend::utils::smtp::SmtpClient; | ||
| /// | ||
| /// let smtp_client = SmtpClient::new("smtp.example.com", 587, "user", "pass").unwrap(); | ||
| /// ``` | ||
| pub fn new( | ||
| smtp_server: &str, | ||
| smtp_port: u16, | ||
| username: &str, | ||
| password: &str, | ||
| ) -> Result<Self, lettre::transport::smtp::Error> { | ||
| let creds = Credentials::new(username.to_string(), password.to_string()); | ||
|
|
||
| let mailer = AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(smtp_server)? | ||
| .port(smtp_port) | ||
| .credentials(creds) | ||
| .build(); | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| Ok(SmtpClient { mailer }) | ||
| } | ||
|
|
||
| /// メールを送信する関数 | ||
| /// | ||
| /// # Arguments | ||
| /// * `from` - 送信元のメールアドレス | ||
| /// * `to` - 送信先のメールアドレス | ||
| /// * `subject` - メールの件名 | ||
| /// * `body_text` - メールのテキスト形式の本文 | ||
| /// * `body_html` - メールのHTML形式の本文(オプション) | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ```no_run | ||
| /// use backend::utils::smtp::SmtpClient; | ||
| /// | ||
| /// # #[tokio::main] | ||
| /// # async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
| /// let client = SmtpClient::new("smtp.example.com", 587, "user", "pass")?; | ||
| /// client.send_email( | ||
| /// "sender@example.com", | ||
| /// "receiver@example.com", | ||
| /// "Hello from Async Rust!", | ||
| /// "This is a test email sent asynchronously.", | ||
| /// Some("<p>This is a <b>test</b> email sent asynchronously.</p>"), | ||
| /// ).await?; | ||
| /// # Ok(()) | ||
| /// # } | ||
| /// ``` | ||
| pub async fn send_email( | ||
| &self, | ||
| from: &str, | ||
| to: &str, | ||
| subject: &str, | ||
| body_text: &str, | ||
| body_html: Option<&str>, | ||
| ) -> Result<(), Box<dyn std::error::Error>> { | ||
| let builder = Message::builder() | ||
| .from(from.parse::<Mailbox>()?) | ||
| .to(to.parse::<Mailbox>()?) | ||
| .subject(subject); | ||
|
|
||
| let email = match body_html { | ||
| Some(html) => builder.multipart( | ||
| MultiPart::alternative() | ||
| .singlepart(SinglePart::plain(body_text.to_string())) | ||
| .singlepart(SinglePart::html(html.to_string())), | ||
| )?, | ||
| None => builder.singlepart(SinglePart::plain(body_text.to_string()))?, | ||
| }; | ||
|
|
||
| // 送信処理にタイムアウトを30秒に設定 | ||
| timeout( | ||
| Duration::from_secs(SMTP_SEND_TIMEOUT_SECS), | ||
| self.mailer.send(email), | ||
| ) | ||
| .await | ||
| .map_err(|_| "SMTP send timeout")??; | ||
|
|
||
| Ok(()) | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.