122 lines
3.7 KiB
Rust
122 lines
3.7 KiB
Rust
use sea_orm::*;
|
|
use rand::distr::Alphanumeric;
|
|
use rand::Rng;
|
|
use crate::models::invite_link::{self, Entity as InviteLink, Model as InviteLinkModel};
|
|
use crate::models::{user, User};
|
|
|
|
pub struct InviteLinkService;
|
|
|
|
impl InviteLinkService {
|
|
pub fn generate_token() -> String {
|
|
rand::rng()
|
|
.sample_iter(&Alphanumeric)
|
|
.take(32)
|
|
.map(char::from)
|
|
.collect()
|
|
}
|
|
|
|
pub async fn create(
|
|
db: &DatabaseConnection,
|
|
family_id: i32,
|
|
created_by: i32,
|
|
expires_at: Option<chrono::NaiveDateTime>,
|
|
max_uses: Option<i32>,
|
|
) -> Result<InviteLinkModel, DbErr> {
|
|
let token = Self::generate_token();
|
|
|
|
let invite = invite_link::ActiveModel {
|
|
family_id: Set(family_id),
|
|
token: Set(token),
|
|
created_by: Set(created_by),
|
|
expires_at: Set(expires_at),
|
|
max_uses: Set(max_uses),
|
|
..Default::default()
|
|
};
|
|
|
|
invite.insert(db).await
|
|
}
|
|
|
|
pub async fn find_by_token(
|
|
db: &DatabaseConnection,
|
|
token: &str,
|
|
) -> Result<Option<InviteLinkModel>, DbErr> {
|
|
InviteLink::find()
|
|
.filter(invite_link::Column::Token.eq(token))
|
|
.one(db)
|
|
.await
|
|
}
|
|
|
|
pub async fn find_by_family(
|
|
db: &DatabaseConnection,
|
|
family_id: i32,
|
|
) -> Result<Vec<InviteLinkModel>, DbErr> {
|
|
InviteLink::find()
|
|
.filter(invite_link::Column::FamilyId.eq(family_id))
|
|
.all(db)
|
|
.await
|
|
}
|
|
|
|
pub async fn validate_and_use(
|
|
db: &DatabaseConnection,
|
|
token: &str,
|
|
user_id: i32,
|
|
) -> Result<InviteLinkModel, DbErr> {
|
|
let invite = InviteLink::find()
|
|
.filter(invite_link::Column::Token.eq(token))
|
|
.one(db)
|
|
.await?
|
|
.ok_or(DbErr::RecordNotFound("Invite link not found".to_string()))?;
|
|
|
|
if let Some(expires_at) = invite.expires_at {
|
|
let now = chrono::Utc::now().naive_utc();
|
|
if now > expires_at {
|
|
return Err(DbErr::Custom("Invite link has expired".to_string()));
|
|
}
|
|
}
|
|
|
|
if let Some(max_uses) = invite.max_uses {
|
|
if invite.uses_count >= max_uses {
|
|
return Err(DbErr::Custom("Invite link has reached max uses".to_string()));
|
|
}
|
|
}
|
|
|
|
let user = User::find_by_id(user_id)
|
|
.one(db)
|
|
.await?
|
|
.ok_or(DbErr::RecordNotFound("User not found".to_string()))?;
|
|
|
|
if user.family_id.is_some() {
|
|
return Err(DbErr::Custom("User already belongs to a family".to_string()));
|
|
}
|
|
|
|
let mut active_user: user::ActiveModel = user.into();
|
|
active_user.family_id = Set(Some(invite.family_id));
|
|
active_user.update(db).await?;
|
|
|
|
let mut active_invite: invite_link::ActiveModel = invite.clone().into();
|
|
active_invite.uses_count = Set(invite.uses_count + 1);
|
|
active_invite.update(db).await
|
|
}
|
|
|
|
pub async fn delete(db: &DatabaseConnection, id: i32) -> Result<DeleteResult, DbErr> {
|
|
let invite = InviteLink::find_by_id(id)
|
|
.one(db)
|
|
.await?
|
|
.ok_or(DbErr::RecordNotFound("Invite link not found".to_string()))?;
|
|
|
|
let invite: invite_link::ActiveModel = invite.into();
|
|
invite.delete(db).await
|
|
}
|
|
|
|
pub async fn delete_by_token(db: &DatabaseConnection, token: &str) -> Result<DeleteResult, DbErr> {
|
|
let invite = InviteLink::find()
|
|
.filter(invite_link::Column::Token.eq(token))
|
|
.one(db)
|
|
.await?
|
|
.ok_or(DbErr::RecordNotFound("Invite link not found".to_string()))?;
|
|
|
|
let invite: invite_link::ActiveModel = invite.into();
|
|
invite.delete(db).await
|
|
}
|
|
}
|