oauth2
This commit is contained in:
121
backend/src/services/invite_link_service.rs
Normal file
121
backend/src/services/invite_link_service.rs
Normal file
@@ -0,0 +1,121 @@
|
||||
use sea_orm::*;
|
||||
use rand::distributions::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::thread_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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user