use crate::{ apub::actions::{ CreateProfile, DeleteProfile, DeleteSubmission, SuspendProfile, UpdateProfile, }, store::Profile, Action, Context, Error, Outbound, Required, }; impl Action for CreateProfile { fn perform(&self, ctx: &Context) -> Result>, Error> { log::debug!("CreateProfile"); let profile = ctx.store.profiles.create( self.owner_source.clone(), &self.handle, &self.domain, self.published, )?; let mut changes = profile.update(); changes.login_required(self.login_required); if let Some(display_name) = &self.display_name { changes.display_name(&hyaenidae_content::html(display_name)); } if let Some(description) = &self.description { changes.description(&hyaenidae_content::html(description)); } if let Some(banner) = self.banner { changes.banner(banner); } if let Some(icon) = self.icon { changes.icon(icon); } let profile = if changes.any_changes() { ctx.store.profiles.update(&changes)? } else { profile }; if let Some(apub) = &self.apub { ctx.apub.profile(&apub.person_apub_id, profile.id())?; ctx.apub.store_profile_public_key( profile.id(), &apub.endpoints.public_key, &apub.public_key, )?; ctx.apub.store_profile_endpoints( profile.id(), &apub.person_apub_id, apub.endpoints.clone(), )?; } if profile.owner_source().is_local() { return Ok(Some(Box::new(crate::apub::results::ProfileCreated { profile_id: profile.id(), }))); } Ok(None) } } impl Action for UpdateProfile { fn perform(&self, ctx: &Context) -> Result>, Error> { log::debug!("UpdateProfile"); let profile = ctx .store .profiles .by_id(self.profile_id)? .req("profile by id")?; let mut changes = profile.update(); if let Some(login_required) = self.login_required { changes.login_required(login_required); } if let Some(display_name) = &self.display_name { changes.display_name(&hyaenidae_content::html(display_name)); } if let Some(display_name_source) = &self.display_name_source { changes.display_name_source(display_name_source); } if let Some(description) = &self.description { changes.description(&hyaenidae_content::html(description)); } if let Some(description_source) = &self.description_source { changes.description_source(description_source); } let previous_banner = profile.banner(); let previous_icon = profile.icon(); if let Some(banner) = self.banner { changes.banner(banner); if let Some(old_banner) = previous_banner { if old_banner != banner { ctx.spawner.purge_file(old_banner); } } } if let Some(icon) = self.icon { changes.icon(icon); if let Some(old_icon) = previous_icon { if old_icon != icon { ctx.spawner.purge_file(old_icon); } } } let profile = if changes.any_changes() { ctx.store.profiles.update(&changes)? } else { profile }; if let Some(apub) = &self.apub { let person_apub_id = ctx .apub .apub_for_profile(profile.id())? .req("apub id for profile")?; ctx.apub.store_profile_public_key( profile.id(), &apub.endpoints.public_key, &apub.public_key, )?; ctx.apub.store_profile_endpoints( profile.id(), &person_apub_id, apub.endpoints.clone(), )?; } if profile.owner_source().is_local() { return Ok(Some(Box::new(crate::apub::results::ProfileUpdated { profile_id: profile.id(), }))); } Ok(None) } } fn delete_submission(submission_id: uuid::Uuid, ctx: &Context) -> Result<(), Error> { Action::perform(&DeleteSubmission { submission_id }, ctx)?; Ok(()) } fn clean_profile(profile: &Profile, ctx: &Context) -> Result<(), Error> { let profile_id = profile.id(); let person_apub_id = ctx .apub .apub_for_profile(profile_id)? .req("apub for profile")?; ctx.apub.delete_object(&person_apub_id)?; if let Some(banner) = profile.banner() { ctx.spawner.purge_file(banner); } if let Some(icon) = profile.icon() { ctx.spawner.purge_file(icon); } let ctx_clone = ctx.clone(); ctx.spawn_blocking(move || { for submission_id in ctx_clone .store .submissions .published_for_profile(profile_id) { if let Err(e) = delete_submission(submission_id, &ctx_clone) { log::error!("Failed to delete submission {}: {}", submission_id, e); } } for submission_id in ctx_clone.store.submissions.drafted_for_profile(profile_id) { if let Err(e) = delete_submission(submission_id, &ctx_clone) { log::error!("Failed to delete submission {}: {}", submission_id, e); } } }); let ctx_clone = ctx.clone(); ctx.spawn_blocking(move || { for follow_id in ctx_clone.store.view.follows.forward_iter(profile_id) { if let Err(e) = ctx_clone.store.view.follows.remove(follow_id) { log::error!("Failed to delete follow {}: {}", follow_id, e); } } }); let ctx_clone = ctx.clone(); ctx.spawn_blocking(move || { for follow_request_id in ctx_clone .store .view .follow_requests .forward_iter(profile_id) { if let Err(e) = ctx_clone .store .view .follow_requests .remove(follow_request_id) { log::error!("Failed to delete follow {}: {}", follow_request_id, e); } } }); Ok(()) } impl Action for DeleteProfile { fn perform(&self, ctx: &Context) -> Result>, Error> { let profile_id = self.profile_id; let opt = ctx.store.profiles.suspend(profile_id)?; if let Some(undo_profile) = opt { clean_profile(&undo_profile.0, ctx)?; if undo_profile.0.owner_source().is_local() { return Ok(Some(Box::new(crate::apub::results::ProfileDeleted { profile_id, }))); } } Ok(None) } } impl Action for SuspendProfile { fn perform(&self, ctx: &Context) -> Result>, Error> { let profile_id = self.profile_id; let opt = ctx.store.profiles.suspend(profile_id)?; if let Some(undo_profile) = opt { clean_profile(&undo_profile.0, ctx)?; } Ok(None) } }