WIP email sending for registration
This commit is contained in:
@@ -18,4 +18,10 @@ pub struct Config {
|
||||
pub max_mem_log_size: usize,
|
||||
#[clap(long, env)]
|
||||
pub content_dir: String,
|
||||
#[clap(long, env)]
|
||||
pub smtp_server: String,
|
||||
#[clap(long, env)]
|
||||
pub smtp_user: String,
|
||||
#[clap(long, env)]
|
||||
pub smtp_password: String,
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use axum::TypedHeader;
|
||||
use axum::{extract::State, Form};
|
||||
use lettre::message::header::ContentType;
|
||||
use lettre::message::{Mailbox, Message};
|
||||
use lettre::{SmtpTransport, Transport};
|
||||
use maud::html;
|
||||
use serde::Deserialize;
|
||||
use serde_with::{serde_as, NoneAsEmptyString};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::htmx::{HXTarget, HXRedirect};
|
||||
use crate::htmx::{HXRedirect, HXTarget};
|
||||
use crate::models::user::{AuthContext, CreateUser, User};
|
||||
use crate::partials::layout::Layout;
|
||||
use crate::partials::register_form::{register_form, RegisterFormProps};
|
||||
@@ -38,6 +41,7 @@ pub async fn get(hx_target: Option<TypedHeader<HXTarget>>, layout: Layout) -> Re
|
||||
|
||||
pub async fn post(
|
||||
State(pool): State<PgPool>,
|
||||
State(mailer): State<SmtpTransport>,
|
||||
mut auth: AuthContext,
|
||||
Form(register): Form<Register>,
|
||||
) -> Result<Response> {
|
||||
@@ -111,6 +115,28 @@ pub async fn post(
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: don't 500 error on email send failure, render form with error message instead
|
||||
let mailbox = Mailbox::new(
|
||||
user.name.clone(),
|
||||
user.email.parse().map_err(|_| Error::InternalServerError)?,
|
||||
);
|
||||
let email = Message::builder()
|
||||
// TODO: make from address configurable and store in config already parsed
|
||||
.from("crawlnicle <accounts@mail.crawlnicle.com>".parse().unwrap())
|
||||
.to(mailbox)
|
||||
.subject("Welcome to crawlnicle, please confirm your email address")
|
||||
.header(ContentType::TEXT_PLAIN)
|
||||
// TODO: fill in email body, use maud to create HTML body
|
||||
.body(String::from("TODO"))
|
||||
.map_err(|_| Error::InternalServerError)?;
|
||||
|
||||
// TODO: do email sending in a background async task
|
||||
// TODO: notify the user that email has been sent somehow
|
||||
mailer
|
||||
.send(&email)
|
||||
.map_err(|_| Error::InternalServerError)?;
|
||||
|
||||
auth.login(&user)
|
||||
.await
|
||||
.map_err(|_| Error::InternalServerError)?;
|
||||
|
||||
11
src/main.rs
11
src/main.rs
@@ -14,6 +14,8 @@ use axum_login::{
|
||||
use bytes::Bytes;
|
||||
use clap::Parser;
|
||||
use dotenvy::dotenv;
|
||||
use lettre::transport::smtp::authentication::Credentials;
|
||||
use lettre::SmtpTransport;
|
||||
use notify::Watcher;
|
||||
use rand::Rng;
|
||||
use reqwest::Client;
|
||||
@@ -78,6 +80,14 @@ async fn main() -> Result<()> {
|
||||
.with_query("select * from users where user_id = $1");
|
||||
let auth_layer = AuthLayer::new(user_store, &secret);
|
||||
|
||||
let creds = Credentials::new(config.smtp_user.clone(), config.smtp_password.clone());
|
||||
|
||||
// Open a remote connection to gmail
|
||||
let mailer = SmtpTransport::relay(&config.smtp_server)
|
||||
.unwrap()
|
||||
.credentials(creds)
|
||||
.build();
|
||||
|
||||
sqlx::migrate!().run(&pool).await?;
|
||||
|
||||
let crawl_scheduler = CrawlSchedulerHandle::new(
|
||||
@@ -128,6 +138,7 @@ async fn main() -> Result<()> {
|
||||
crawl_scheduler,
|
||||
importer,
|
||||
imports,
|
||||
mailer,
|
||||
})
|
||||
.layer(ServiceBuilder::new().layer(TraceLayer::new_for_http()))
|
||||
.layer(auth_layer)
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::sync::Arc;
|
||||
|
||||
use axum::extract::FromRef;
|
||||
use bytes::Bytes;
|
||||
use lettre::SmtpTransport;
|
||||
use reqwest::Client;
|
||||
use sqlx::PgPool;
|
||||
use tokio::sync::{broadcast, watch, Mutex};
|
||||
@@ -47,6 +48,7 @@ pub struct AppState {
|
||||
pub crawl_scheduler: CrawlSchedulerHandle,
|
||||
pub importer: ImporterHandle,
|
||||
pub imports: Imports,
|
||||
pub mailer: SmtpTransport,
|
||||
}
|
||||
|
||||
impl FromRef<AppState> for PgPool {
|
||||
@@ -102,3 +104,9 @@ impl FromRef<AppState> for Imports {
|
||||
state.imports.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRef<AppState> for SmtpTransport {
|
||||
fn from_ref(state: &AppState) -> Self {
|
||||
state.mailer.clone()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user