tinkr 0.0.43

Tinkr is a web framework for quickly building full-stack web applications with Leptos.
Documentation
use chrono::Utc;
use crate::EmailAddress;
use partial_struct::Partial;
use serde::{Deserialize, Serialize};

#[cfg(feature = "ssr")]
use crate::db_init;

use crate::{AppError, date_utils::parse_surrealdb_datetime_to_chrono};

#[cfg(feature = "ssr")]
use uuid::Uuid;

#[cfg(feature = "ssr")]
use surrealdb::{Datetime, RecordId};

#[derive(Debug, Clone, Serialize, Deserialize, Partial)]
#[partial(
    "CreateVerificationToken",
    derive(Serialize, Deserialize),
    omit(id, token, expires)
)]
#[partial(
    "CreateVerificationTokenConent",
    derive(Serialize, Deserialize),
    omit(id)
)]
pub struct VerificationToken {
    pub id: RecordId,
    pub email: EmailAddress,
    pub user_id: RecordId,
    pub expires: Datetime,
    pub token: String,
}

impl VerificationToken {
    pub async fn create_verification_token(
        content: CreateVerificationToken,
    ) -> Result<VerificationToken, AppError> {
        let client = db_init().await?;

        let newtoken_string = Uuid::new_v4().to_string();

        let mut query = client.query("CREATE verificationToken SET email = $email, expires = time::now() + 30m, token = $tokenstring, user_id = $user_id;")
            .bind(("email", content.email))
            .bind(("tokenstring", newtoken_string))
            .bind(("user_id", content.user_id))
            .await?;

        let result: Option<Self> = query.take(0)?;

        match result {
            Some(token) => Ok(token),
            None => Err(AppError::AuthError(
                "Could not create verification token".into(),
            )),
        }
    }

    pub async fn use_verification_token(token: String) -> Result<Self, AppError> {
        let client = db_init().await?;

        let mut result = client
            .query("DELETE verificationToken WHERE token = $tokenstring RETURN BEFORE;")
            .bind(("tokenstring", token))
            .await?;

        let token: Option<Self> = result.take(0)?;

        tracing::info!("use_verification_token result: {:#?}", token);

        match token {
            Some(token) => {
                let expired = parse_surrealdb_datetime_to_chrono(&token.expires)
                    .ok_or_else(|| AppError::AuthError("Invalid token date".into()))?;

                if expired < Utc::now() {
                    return Err(AppError::AuthError("Token has expired".into()));
                }
                Ok(token)
            }
            None => return Err(AppError::AuthError("Token not found".into())),
        }
    }
}