/
subscription.rs
97 lines (84 loc) · 2.67 KB
/
subscription.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use crate::{domain::*, subscription_tokens, subscriptions, AppError, AppState, BadRequestError};
use crate::subscription_tokens::Model;
use axum::{
extract::{Form, State},
http::StatusCode,
};
use sea_orm::{ActiveModelTrait, Set, TransactionTrait};
use serde::Deserialize;
use tracing::instrument;
use utoipa::ToSchema;
use uuid::Uuid;
#[derive(Deserialize, Debug, ToSchema)]
pub struct NewSubscriber {
/// Subscriber Email
pub email: Option<String>,
/// Subscriber Name
pub name: Option<String>,
}
#[utoipa::path(
post,
path = "/subscriptions",
tag = "subscription",
request_body(
content = NewSubscriber,
content_type = "application/x-www-form-urlencoded"),
responses(
(status = 200),
(status = 400)
))]
#[instrument]
pub async fn subscribe(
state: State<AppState>,
Form(data): Form<NewSubscriber>,
) -> Result<StatusCode, AppError> {
let new_subscriber = Subscriber::try_from(data)?;
let txn = state.database.begin().await?;
let subscriber = subscriptions::ActiveModel {
id: Set(Uuid::new_v4()),
email: Set(new_subscriber.email.as_ref().to_owned()),
name: Set(new_subscriber.name.as_ref().to_owned()),
subscribed_at: Set(chrono::Utc::now().into()),
..Default::default()
}
.insert(&txn)
.await?;
let subscription_token = subscription_tokens::ActiveModel {
subscriber_id: Set(subscriber.id),
subscription_token: Set(Uuid::new_v4().to_string()),
}
.insert(&txn)
.await?;
let html_body = get_email_content(&state, subscription_token);
_ = &state
.email_client
.send_email(&new_subscriber.email, "Welcome!", &html_body)
.await?;
txn.commit().await?;
Ok(StatusCode::OK)
}
fn get_email_content(state: &State<AppState>, subscription_token: Model) -> String {
let confirmation_link_relative = format!(
"/subscriptions/confirm?subscription_token={}",
subscription_token.subscription_token
);
let confirmation_link = &state
.base_url
.clone()
.join(&confirmation_link_relative)
.unwrap();
let html_body = format!(
"Welcome to our newsletter!<br />\
Click <a href=\"{}\">here</a> to confirm your subscription.",
confirmation_link
);
html_body
}
impl TryFrom<NewSubscriber> for Subscriber {
type Error = BadRequestError;
fn try_from(value: NewSubscriber) -> Result<Self, BadRequestError> {
let name = SubscriberName::parse(value.name.unwrap_or_default())?;
let email = SubscriberEmail::parse(value.email.unwrap_or_default())?;
Ok(Subscriber { email, name })
}
}