Files
family_budget/backend/src/services/invite_link_service.rs
2026-01-24 18:06:14 +03:00

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
}
}