Final push.

This commit is contained in:
LukeMathWalker 2021-03-11 23:11:38 +00:00
parent ccf1d84fe9
commit b21e9cc99c
2 changed files with 88 additions and 11 deletions

View file

@ -43,11 +43,14 @@ pub async fn subscribe(
.0 .0
.try_into() .try_into()
.map_err(|_| HttpResponse::BadRequest().finish())?; .map_err(|_| HttpResponse::BadRequest().finish())?;
insert_subscriber(&pool, &new_subscriber) let subscriber_id = insert_subscriber(&pool, &new_subscriber)
.await .await
.map_err(|_| HttpResponse::InternalServerError().finish())?; .map_err(|_| HttpResponse::InternalServerError().finish())?;
// We are swallowing the error for the time being. // We are swallowing the error for the time being.
let subscription_token = generate_subscription_token(); let subscription_token = generate_subscription_token();
store_token(&pool, subscriber_id, &subscription_token)
.await
.map_err(|_| HttpResponse::InternalServerError().finish())?;
let _ = send_confirmation_email( let _ = send_confirmation_email(
&email_client, &email_client,
new_subscriber, new_subscriber,
@ -100,13 +103,14 @@ pub async fn send_confirmation_email(
pub async fn insert_subscriber( pub async fn insert_subscriber(
pool: &PgPool, pool: &PgPool,
new_subscriber: &NewSubscriber, new_subscriber: &NewSubscriber,
) -> Result<(), sqlx::Error> { ) -> Result<Uuid, sqlx::Error> {
let subscriber_id = Uuid::new_v4();
sqlx::query!( sqlx::query!(
r#" r#"
INSERT INTO subscriptions (id, email, name, subscribed_at, status) INSERT INTO subscriptions (id, email, name, subscribed_at, status)
VALUES ($1, $2, $3, $4, 'pending_confirmation') VALUES ($1, $2, $3, $4, 'pending_confirmation')
"#, "#,
Uuid::new_v4(), subscriber_id,
new_subscriber.email.as_ref(), new_subscriber.email.as_ref(),
new_subscriber.name.as_ref(), new_subscriber.name.as_ref(),
Utc::now() Utc::now()
@ -117,5 +121,31 @@ pub async fn insert_subscriber(
tracing::error!("Failed to execute query: {:?}", e); tracing::error!("Failed to execute query: {:?}", e);
e e
})?; })?;
Ok(subscriber_id)
}
#[tracing::instrument(
name = "Store subscription token in the database",
skip(subscription_token, pool)
)]
pub async fn store_token(
pool: &PgPool,
subscriber_id: Uuid,
subscription_token: &str,
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
INSERT INTO subscription_tokens (subscription_token, subscriber_id)
VALUES ($1, $2)
"#,
subscription_token,
subscriber_id
)
.execute(pool)
.await
.map_err(|e| {
tracing::error!("Failed to execute query: {:?}", e);
e
})?;
Ok(()) Ok(())
} }

View file

@ -1,14 +1,61 @@
use actix_web::{HttpResponse, web}; use actix_web::{web, HttpResponse};
use sqlx::PgPool;
use uuid::Uuid;
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
pub struct Parameters { pub struct Parameters {
subscription_token: String subscription_token: String,
} }
#[tracing::instrument( #[tracing::instrument(name = "Confirm a pending subscriber", skip(parameters, pool))]
name = "Confirm a pending subscriber", pub async fn confirm(
skip(_parameters) parameters: web::Query<Parameters>,
)] pool: web::Data<PgPool>,
pub async fn confirm(_parameters: web::Query<Parameters>) -> HttpResponse { ) -> Result<HttpResponse, HttpResponse> {
HttpResponse::Ok().finish() let id = get_subscriber_id_from_token(&pool, &parameters.subscription_token)
.await
.map_err(|_| HttpResponse::InternalServerError().finish())?;
match id {
// Non-existing token!
None => Err(HttpResponse::Unauthorized().finish()),
Some(subscriber_id) => {
confirm_subscriber(&pool, subscriber_id)
.await
.map_err(|_| HttpResponse::InternalServerError().finish())?;
Ok(HttpResponse::Ok().finish())
}
}
}
#[tracing::instrument(name = "Mark subscriber as confirmed", skip(subscriber_id, pool))]
pub async fn confirm_subscriber(pool: &PgPool, subscriber_id: Uuid) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"UPDATE subscriptions SET status = 'confirmed' WHERE id = $1"#,
subscriber_id,
)
.execute(pool)
.await
.map_err(|e| {
tracing::error!("Failed to execute query: {:?}", e);
e
})?;
Ok(())
}
#[tracing::instrument(name = "Get subscriber_id from token", skip(subscription_token, pool))]
pub async fn get_subscriber_id_from_token(
pool: &PgPool,
subscription_token: &str,
) -> Result<Option<Uuid>, sqlx::Error> {
let result = sqlx::query!(
r#"SELECT subscriber_id FROM subscription_tokens WHERE subscription_token = $1"#,
subscription_token,
)
.fetch_optional(pool)
.await
.map_err(|e| {
tracing::error!("Failed to execute query: {:?}", e);
e
})?;
Ok(result.map(|r| r.subscriber_id))
} }