magic token login

This commit is contained in:
2025-10-31 15:16:30 +01:00
parent 91dd3289a9
commit 48bed9a9fe
10 changed files with 719 additions and 92 deletions
+77 -1
View File
@@ -10,7 +10,9 @@ use std::io::Write;
use std::str;
use uuid::Uuid;
use crate::schema::sql_types::TenantStatus as TenantStatusSql;
use crate::schema::sql_types::{
MagicTokenKind as MagicTokenKindSql, TenantStatus as TenantStatusSql,
};
use crate::schema::*;
#[derive(Debug, Clone, Queryable, Identifiable, Associations)]
@@ -43,6 +45,64 @@ pub enum TenantStatus {
Error,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, AsExpression, FromSqlRow)]
#[diesel(sql_type = MagicTokenKindSql)]
pub enum MagicTokenKind {
EmailLogin,
DemoLogin,
}
impl MagicTokenKind {
pub fn as_str(&self) -> &'static str {
match self {
MagicTokenKind::EmailLogin => "email_login",
MagicTokenKind::DemoLogin => "demo_login",
}
}
pub fn variants() -> &'static [&'static str] {
&["email_login", "demo_login"]
}
}
impl fmt::Display for MagicTokenKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl ToSql<MagicTokenKindSql, Pg> for MagicTokenKind {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
out.write_all(self.as_str().as_bytes())?;
Ok(IsNull::No)
}
}
impl FromSql<MagicTokenKindSql, Pg> for MagicTokenKind {
fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
match std::str::from_utf8(bytes.as_bytes())? {
"email_login" => Ok(MagicTokenKind::EmailLogin),
"demo_login" => Ok(MagicTokenKind::DemoLogin),
other => Err(Box::new(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("invalid magic_token_kind '{other}'"),
))),
}
}
}
impl str::FromStr for MagicTokenKind {
type Err = &'static str;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"email_login" => Ok(MagicTokenKind::EmailLogin),
"demo_login" => Ok(MagicTokenKind::DemoLogin),
_ => Err("unsupported magic token kind"),
}
}
}
impl TenantStatus {
pub fn as_str(&self) -> &'static str {
match self {
@@ -265,6 +325,22 @@ pub struct NewDocument {
pub tenant_id: Uuid,
}
#[derive(Debug, Clone, Queryable, Identifiable, Insertable)]
#[diesel(table_name = magic_tokens)]
pub struct MagicToken {
pub id: Uuid,
pub user_id: Uuid,
pub kind: MagicTokenKind,
pub token_hash: String,
pub metadata: serde_json::Value,
pub expires_at: NaiveDateTime,
pub max_uses: Option<i32>,
pub used_count: i32,
pub created_at: NaiveDateTime,
pub created_by: Option<Uuid>,
pub last_used_at: Option<NaiveDateTime>,
}
#[derive(Debug, Clone, Queryable, Identifiable, Associations)]
#[diesel(table_name = document_versions)]
#[diesel(belongs_to(Document))]