Files
bichon/crates/core/src/message/append.rs

86 lines
2.5 KiB
Rust

use crate::{
encode_mailbox_name, raise_error,
{
account::migration::{AccountModel, AccountType},
envelope::extractor::reattach_eml_content,
error::{code::ErrorCode, BichonResult},
imap::executor::ImapExecutor,
},
};
//use poem_openapi::Object;
use serde::{Deserialize, Serialize};
const MAX_RESTORE_COUNT: usize = 100;
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
#[cfg_attr(feature = "web-api", derive(poem_openapi::Object))]
pub struct RestoreMessagesRequest {
/// envelope IDs to restore (max 100)
pub envelope_ids: Vec<String>,
}
pub async fn restore_emails(account_id: u64, envelope_ids: Vec<String>) -> BichonResult<()> {
if envelope_ids.len() > MAX_RESTORE_COUNT {
return Err(raise_error!(
format!(
"Too many messages to restore: {} (max {})",
envelope_ids.len(),
MAX_RESTORE_COUNT
),
ErrorCode::InvalidParameter
));
}
let account = AccountModel::check_account_exists(account_id)?;
if !matches!(account.account_type, AccountType::IMAP) {
return Err(raise_error!(
"Account type is not IMAP".into(),
ErrorCode::Incompatible
));
}
let mut failed = Vec::new();
let mut session = ImapExecutor::create_connection(account_id).await?;
for envelope_id in envelope_ids {
let result: BichonResult<()> = async {
let (envelope, eml) = reattach_eml_content(account_id, envelope_id.clone())?;
if let Some(mailbox_name) = envelope.mailbox_name {
ImapExecutor::append(
&mut session,
encode_mailbox_name!(&mailbox_name),
None,
None,
&eml,
)
.await?;
}
Ok(())
}
.await;
if let Err(err) = result {
tracing::warn!(
account_id = account_id,
message_id = &envelope_id,
error = ?err,
"Failed to restore email"
);
failed.push(envelope_id);
}
}
if !failed.is_empty() {
tracing::info!(
account_id = account_id,
failed_count = failed.len(),
failed_message_ids = ?failed,
"Restore emails finished with partial failures"
);
}
session.logout().await.ok();
Ok(())
}