fix: account deletion times out #291

This commit is contained in:
rustmailer
2026-06-07 15:34:38 +08:00
parent 62cb5264fd
commit c736afffb0
29 changed files with 106 additions and 14 deletions
+43 -8
View File
@@ -97,6 +97,8 @@ pub struct Account {
pub imap_quota_window: Option<QuotaWindow>,
pub auto_download_new_mailboxes: Option<bool>,
pub download_schedule: Option<String>,
#[serde(default)]
pub deleting: bool,
}
impl MemDbModel for Account {
@@ -136,6 +138,7 @@ impl Account {
imap_quota_bytes: request.imap_quota_bytes,
imap_quota_window: request.imap_quota_window,
download_schedule: request.download_schedule,
deleting: false,
})
}
@@ -223,14 +226,46 @@ impl Account {
pub async fn delete(account_id: u64) -> BichonResult<()> {
let account = Self::get(account_id)?;
if let Err(error) = Self::cleanup_account_resources_sequential(&account).await {
tracing::error!(
"[CLEANUP_ACCOUNT_ERROR] Account {}: failed to cleanup resources: {:#?}",
account_id,
error
);
return Err(error);
// Immediately stop scheduling to prevent new downloads
if matches!(account.account_type, AccountType::IMAP) {
SYNC_TASKS.stop(account.id).await?;
}
// Mark as deleting and disabled so frontend shows status and download tasks skip it
update_impl(
DB_MANAGER.db(),
&account_id.to_string(),
move |current: Account| {
let mut updated = current.clone();
updated.deleting = true;
updated.enabled = false;
Ok(updated)
},
)?;
// Spawn background cleanup — heavy work (Tantivy, attachments) runs off the request path
tokio::spawn(async move {
if let Err(error) = Self::cleanup_account_resources_sequential(&account).await {
tracing::error!(
"[CLEANUP_ACCOUNT_ERROR] Account {}: cleanup failed, reverting deleting flag: {:#?}",
account_id,
error
);
// Revert deleting flag so the user can retry (only if account record still exists)
let _ = update_impl(
DB_MANAGER.db(),
&account_id.to_string(),
move |current: Account| {
let mut updated = current.clone();
updated.deleting = false;
updated.enabled = true;
Ok(updated)
},
);
}
});
Ok(())
}
@@ -239,8 +274,8 @@ impl Account {
}
async fn cleanup_account_resources_sequential(account: &AccountModel) -> BichonResult<()> {
// Sync task already stopped in delete() before spawning this background task
if matches!(account.account_type, AccountType::IMAP) {
SYNC_TASKS.stop(account.id).await?;
DownloadState::delete(account.id)?;
}
OAuth2AccessToken::try_delete(account.id)?;
+2
View File
@@ -58,6 +58,7 @@ pub struct AccountResp {
pub imap_quota_window: Option<QuotaWindow>,
pub auto_download_new_mailboxes: Option<bool>,
pub download_schedule: Option<String>,
pub deleting: bool,
}
impl AccountResp {
@@ -95,6 +96,7 @@ impl AccountResp {
imap_quota_window: account.imap_quota_window,
auto_download_new_mailboxes: account.auto_download_new_mailboxes,
download_schedule: account.download_schedule,
deleting: account.deleting,
}
}
}
+7
View File
@@ -113,6 +113,9 @@ impl AccountDownTask {
let account = AccountModel::get(account_id).ok();
match account {
Some(account) => {
if account.deleting {
return Ok(());
}
if !account.enabled {
let last = LAST_WARN_TIME.load(Ordering::Relaxed);
let now = utc_now!();
@@ -246,6 +249,10 @@ impl AccountDownTask {
}
};
if account.deleting {
return;
}
if let Err(e) = process_imap_download(&account, token_clone, TriggerType::Manual).await
{
error!("Manual download failed for {}: {:?}", account_id, e);