tallyweb-backend 0.3.1

backend for the tallyweb site
Documentation
use super::*;
use sqlx::{query, query_as, PgPool};

#[derive(Debug, Clone, sqlx::Type)]
#[sqlx(type_name = "hunttype")]
pub enum Hunttype {
    OldOdds,
    NewOdds,
    SOS,
    DexNav,
    MasudaGenIV,
    MasudaGenV,
    MasudaGenVI,
}

impl From<String> for Hunttype {
    fn from(value: String) -> Self {
        match value.as_str() {
            "OldOdds" => Self::OldOdds,
            "NewOdds" => Self::NewOdds,
            "SOS" => Self::SOS,
            "DexNav" => Self::DexNav,
            "MasudaGenIV" => Self::MasudaGenIV,
            "MasudaGenV" => Self::MasudaGenV,
            "MasudaGenVI" => Self::MasudaGenVI,
            _ => Self::NewOdds,
        }
    }
}

#[derive(Debug, sqlx::FromRow)]
pub struct DbCounter {
    pub uuid: uuid::Uuid,
    pub owner_uuid: uuid::Uuid,
    pub name: String,
    pub created_at: chrono::NaiveDateTime,
    pub last_edit: chrono::NaiveDateTime,
    pub is_deleted: bool,
}

#[derive(Debug, sqlx::FromRow)]
pub struct DbPhase {
    pub uuid: uuid::Uuid,
    pub owner_uuid: uuid::Uuid,
    pub parent_uuid: uuid::Uuid,
    pub name: String,
    pub count: i32,
    pub time: i64,
    pub hunt_type: Hunttype,
    pub has_charm: bool,
    pub dexnav_encounters: Option<i32>,
    pub success: bool,
    pub created_at: chrono::NaiveDateTime,
    pub last_edit: chrono::NaiveDateTime,
    pub is_deleted: bool,
}

#[derive(Debug)]
pub struct DbUser {
    pub uuid: uuid::Uuid,
    pub username: String,
    pub token: Option<uuid::Uuid>,
    pub token_expire: chrono::NaiveDateTime,
    pub email: Option<String>,
}

impl DbUser {
    pub async fn get_token(&self, pool: &PgPool) -> Result<DbAuthToken, sqlx::error::Error> {
        let token = query_as!(
            DbAuthToken,
            r#"
            select * from auth_tokens
            where uuid = $1 AND user_uuid = $2
            "#,
            self.token,
            self.uuid,
        )
        .fetch_one(pool)
        .await?;
        Ok(token)
    }

    pub async fn token_status(&self, pool: &PgPool) -> TokenStatus {
        if let Ok(token) = self.get_token(pool).await {
            if token.expire_on.and_utc() > chrono::Utc::now() {
                TokenStatus::Valid
            } else {
                TokenStatus::Expired
            }
        } else {
            TokenStatus::Invalid
        }
    }

    pub async fn get_counters(&self, pool: &PgPool) -> Result<Vec<DbCounter>, AuthorizationError> {
        let data = query_as!(
            DbCounter,
            r#"
            select * from counters
            where owner_uuid = $1
            "#,
            self.uuid
        )
        .fetch_all(pool)
        .await
        .map_err(|err| AuthorizationError::Internal(err.to_string()))?;

        Ok(data)
    }
}

pub struct DbAuthToken {
    pub uuid: uuid::Uuid,
    pub user_uuid: uuid::Uuid,
    pub expire_on: chrono::NaiveDateTime,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TokenStatus {
    Valid,
    Invalid,
    Expired,
}

pub struct DbPreferences {
    pub user_uuid: uuid::Uuid,
    pub use_default_accent_color: bool,
    pub accent_color: Option<String>,
    pub show_separator: bool,
    pub multi_select: bool,
    pub save_on_pause: bool,
}

impl DbPreferences {
    pub async fn db_get(pool: &PgPool, user_uuid: uuid::Uuid) -> Result<Self, BackendError> {
        let data = match query_as!(
            DbPreferences,
            r#"
            select * from preferences
            where user_uuid = $1
            "#,
            user_uuid,
        )
        .fetch_one(pool)
        .await
        {
            Ok(data) => data,
            Err(sqlx::Error::RowNotFound) => {
                Err(BackendError::DataNotFound(String::from("preferences")))?
            }
            Err(err) => Err(err)?,
        };

        Ok(data)
    }

    pub async fn db_set(
        self,
        pool: &PgPool,
        username: &str,
        token: uuid::Uuid,
    ) -> Result<(), BackendError> {
        let user = auth::get_user(pool, username, token).await?;
        query!(
            r#"
            INSERT INTO preferences (
                user_uuid,
                use_default_accent_color,
                accent_color,
                show_separator,
                multi_select,
                save_on_pause
            )
            VALUES ($1, $2, $3, $4, $5, $6)
            ON CONFLICT (user_uuid) DO UPDATE
                SET use_default_accent_color = $2,
                    accent_color = $3,
                    show_separator = $4,
                    multi_select = $5,
                    save_on_pause = $6
            "#,
            user.uuid,
            self.use_default_accent_color,
            self.accent_color,
            self.show_separator,
            self.multi_select,
            self.save_on_pause,
        )
        .execute(pool)
        .await?;

        Ok(())
    }
}