use crate::{ routes::{AppError, Query, generate_token, not_found_html}, startup::AppState, templates::{ConfirmTemplate, HtmlTemplate}, }; use anyhow::Context; use axum::{ extract::State, response::{IntoResponse, Response}, }; use serde::Deserialize; use sqlx::PgPool; use uuid::Uuid; #[tracing::instrument(name = "Confirming new subscriber", skip_all)] pub async fn confirm( State(AppState { connection_pool, .. }): State, Query(params): Query, ) -> Result { let subscriber_id = get_subscriber_id_from_token(&connection_pool, ¶ms.subscription_token) .await .context("Could not fetch subscriber id given subscription token.")?; if let Some(id) = subscriber_id { confirm_subscriber(&connection_pool, &id) .await .context("Failed to update subscriber status.")?; let template = HtmlTemplate(ConfirmTemplate); Ok(template.into_response()) } else { Ok(not_found_html()) } } #[tracing::instrument(name = "Mark subscriber as confirmed", skip(connection_pool))] async fn confirm_subscriber( connection_pool: &PgPool, subscriber_id: &Uuid, ) -> Result<(), sqlx::Error> { sqlx::query!( "UPDATE subscriptions SET status = 'confirmed', unsubscribe_token = $1 WHERE id = $2", generate_token(), subscriber_id ) .execute(connection_pool) .await?; Ok(()) } #[tracing::instrument(name = "Get subscriber id from token", skip(connection))] async fn get_subscriber_id_from_token( connection: &PgPool, subscription_token: &str, ) -> Result, sqlx::Error> { let saved = sqlx::query!( "SELECT subscriber_id FROM subscription_tokens WHERE subscription_token = $1", subscription_token ) .fetch_optional(connection) .await?; Ok(saved.map(|r| r.subscriber_id)) } #[derive(Debug, Deserialize)] pub struct Params { subscription_token: String, }