2021-05-12 21:33:43 +00:00
|
|
|
use crate::routes::error_chain_fmt;
|
|
|
|
use actix_web::http::StatusCode;
|
|
|
|
use actix_web::{web, HttpResponse, ResponseError};
|
|
|
|
use anyhow::Context;
|
2021-03-11 23:11:38 +00:00
|
|
|
use sqlx::PgPool;
|
|
|
|
use uuid::Uuid;
|
2021-03-11 09:24:57 +00:00
|
|
|
|
|
|
|
#[derive(serde::Deserialize)]
|
|
|
|
pub struct Parameters {
|
2021-03-11 23:11:38 +00:00
|
|
|
subscription_token: String,
|
2021-03-11 09:24:57 +00:00
|
|
|
}
|
|
|
|
|
2021-05-12 21:33:43 +00:00
|
|
|
#[derive(thiserror::Error)]
|
|
|
|
pub enum ConfirmationError {
|
|
|
|
#[error(transparent)]
|
|
|
|
UnexpectedError(#[from] anyhow::Error),
|
|
|
|
#[error("There is no subscriber associated with the provided token.")]
|
|
|
|
UnknownToken,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Debug for ConfirmationError {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
error_chain_fmt(self, f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ResponseError for ConfirmationError {
|
|
|
|
fn status_code(&self) -> StatusCode {
|
|
|
|
match self {
|
|
|
|
Self::UnknownToken => StatusCode::UNAUTHORIZED,
|
|
|
|
Self::UnexpectedError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-11 23:11:38 +00:00
|
|
|
#[tracing::instrument(name = "Confirm a pending subscriber", skip(parameters, pool))]
|
|
|
|
pub async fn confirm(
|
|
|
|
parameters: web::Query<Parameters>,
|
|
|
|
pool: web::Data<PgPool>,
|
2021-05-12 21:33:43 +00:00
|
|
|
) -> Result<HttpResponse, ConfirmationError> {
|
|
|
|
let subscriber_id = get_subscriber_id_from_token(&pool, ¶meters.subscription_token)
|
2021-03-11 23:11:38 +00:00
|
|
|
.await
|
2021-05-12 21:33:43 +00:00
|
|
|
.context("Failed to retrieve the subscriber id associated with the provided token.")?
|
|
|
|
.ok_or(ConfirmationError::UnknownToken)?;
|
|
|
|
confirm_subscriber(&pool, subscriber_id)
|
|
|
|
.await
|
|
|
|
.context("Failed to update the subscriber status to `confirmed`.")?;
|
|
|
|
Ok(HttpResponse::Ok().finish())
|
2021-03-11 23:11:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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)
|
2021-05-12 21:12:41 +00:00
|
|
|
.await?;
|
2021-03-11 23:11:38 +00:00
|
|
|
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)
|
2021-05-12 21:12:41 +00:00
|
|
|
.await?;
|
2021-03-11 23:11:38 +00:00
|
|
|
Ok(result.map(|r| r.subscriber_id))
|
2021-03-11 09:24:57 +00:00
|
|
|
}
|