use crate::constants::{StaticConstraints, FAMILY_INVITATION_CODE_LEN}; use crate::controllers::HttpResult; use crate::extractors::family_extractor::{FamilyInPath, FamilyInPathWithAdminMembership}; use crate::services::login_token_service::LoginToken; use crate::services::rate_limiter_service::RatedAction; use crate::services::{families_service, rate_limiter_service}; use crate::utils::string_utils::rand_str; use actix_remote_ip::RemoteIP; use actix_web::{web, HttpResponse}; #[derive(Debug, serde::Deserialize)] pub struct CreateFamilyReq { name: String, } /// Create a new family pub async fn create(req: web::Json, token: LoginToken) -> HttpResult { if !StaticConstraints::default() .family_name_len .validate(&req.name) { return Ok(HttpResponse::BadRequest().body("Invalid family name!")); } let family = families_service::create(&req.name, token.user_id).await?; Ok(HttpResponse::Created().json(family)) } #[derive(Debug, serde::Deserialize)] pub struct JoinFamilyReq { code: String, } /// Join a family pub async fn join( remote_ip: RemoteIP, req: web::Json, token: LoginToken, ) -> HttpResult { // Rate limiting if rate_limiter_service::should_block_action(remote_ip.0, RatedAction::JoinFamily).await? { return Ok(HttpResponse::TooManyRequests().finish()); } rate_limiter_service::record_action(remote_ip.0, RatedAction::JoinFamily).await?; let family = match families_service::get_by_invitation_code(&req.code).await { Ok(f) => f, Err(e) => { log::error!("Could not find family by invitation code! {e}"); return Ok(HttpResponse::NotFound().finish()); } }; if families_service::is_member(family.id(), token.user_id).await? { log::error!( "Could not add {:?} to family {:?} because it is already a member of the family!", token.user_id, family.id() ); return Ok(HttpResponse::Conflict().finish()); } families_service::add_member(family.id(), token.user_id, false).await?; Ok(HttpResponse::Accepted().finish()) } /// Get the list of families of the user pub async fn list(token: LoginToken) -> HttpResult { Ok( HttpResponse::Ok() .json(families_service::get_user_family_memberships(token.user_id).await?), ) } /// Get the information of a single family pub async fn single_info(f: FamilyInPath) -> HttpResult { Ok(HttpResponse::Ok() .json(families_service::get_family_membership(f.family_id(), f.user_id()).await?)) } /// Attempt to leave a family pub async fn leave(f: FamilyInPath) -> HttpResult { families_service::remove_membership(f.family_id(), f.user_id()).await?; Ok(HttpResponse::Accepted().finish()) } #[derive(serde::Deserialize)] pub struct UpdateFamilyBody { name: String, } /// Update a family pub async fn update( f: FamilyInPathWithAdminMembership, req: web::Json, ) -> HttpResult { let mut family = families_service::get_by_id(f.family_id()).await?; family.name = req.0.name; families_service::update_family(&family).await?; log::info!("User {:?} updated family {:?}", f.user_id(), f.family_id()); Ok(HttpResponse::Accepted().finish()) } /// Delete a family pub async fn delete(f: FamilyInPathWithAdminMembership) -> HttpResult { families_service::delete_family(f.family_id()).await?; log::info!("User {:?} deleted family {:?}", f.user_id(), f.family_id()); Ok(HttpResponse::Accepted().finish()) } /// Renew (change) invitation code pub async fn renew_invitation_code(f: FamilyInPathWithAdminMembership) -> HttpResult { let mut family = families_service::get_by_id(f.family_id()).await?; family.invitation_code = rand_str(FAMILY_INVITATION_CODE_LEN); families_service::update_family(&family).await?; log::info!( "User {:?} changed family {:?} invitation code", f.user_id(), f.family_id() ); Ok(HttpResponse::Accepted().finish()) } /// Get the list of users who belongs to a family pub async fn users(f: FamilyInPath) -> HttpResult { Ok(HttpResponse::Ok().json(families_service::get_memberships_of_family(f.family_id()).await?)) }