mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-03 07:48:34 +02:00
refactor: use UUID for envelope id to prevent accidental deletion
This commit is contained in:
@@ -5,7 +5,6 @@ use crate::{
|
||||
error::{code::ErrorCode, BichonResult},
|
||||
imap::executor::ImapExecutor,
|
||||
indexer::manager::{EML_INDEX_MANAGER, ENVELOPE_INDEX_MANAGER},
|
||||
utils::create_hash,
|
||||
},
|
||||
raise_error,
|
||||
};
|
||||
@@ -16,16 +15,16 @@ const MAX_RESTORE_COUNT: usize = 100;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize, Object)]
|
||||
pub struct RestoreMessagesRequest {
|
||||
/// Message IDs to restore (max 100)
|
||||
pub message_ids: Vec<u64>,
|
||||
/// envelope IDs to restore (max 100)
|
||||
pub envelope_ids: Vec<String>,
|
||||
}
|
||||
|
||||
pub async fn restore_emails(account_id: u64, message_ids: Vec<u64>) -> BichonResult<()> {
|
||||
if message_ids.len() > MAX_RESTORE_COUNT {
|
||||
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 {})",
|
||||
message_ids.len(),
|
||||
envelope_ids.len(),
|
||||
MAX_RESTORE_COUNT
|
||||
),
|
||||
ErrorCode::InvalidParameter
|
||||
@@ -42,27 +41,30 @@ pub async fn restore_emails(account_id: u64, message_ids: Vec<u64>) -> BichonRes
|
||||
|
||||
let mut failed = Vec::new();
|
||||
let mut session = ImapExecutor::create_connection(account_id).await?;
|
||||
for message_id in message_ids {
|
||||
for envelope_id in envelope_ids {
|
||||
let result: BichonResult<()> = async {
|
||||
let eid = envelope_id.clone();
|
||||
let envelope = ENVELOPE_INDEX_MANAGER
|
||||
.get_envelope_by_id(account_id, message_id)
|
||||
.get_envelope_by_id(account_id, eid)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
raise_error!(
|
||||
format!(
|
||||
"Envelope not found: account_id={} message_id={}",
|
||||
account_id, message_id
|
||||
account_id, &envelope_id
|
||||
),
|
||||
ErrorCode::ResourceNotFound
|
||||
)
|
||||
})?;
|
||||
let eml_id = create_hash(account_id, &envelope.message_id);
|
||||
let eml = EML_INDEX_MANAGER
|
||||
.get(account_id, eml_id)
|
||||
.get(account_id, &envelope.content_hash)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
raise_error!(
|
||||
format!("Eml not found: account_id={} id={}", account_id, message_id),
|
||||
format!(
|
||||
"Eml not found: account_id={} id={}",
|
||||
account_id, &envelope_id
|
||||
),
|
||||
ErrorCode::ResourceNotFound
|
||||
)
|
||||
})?;
|
||||
@@ -83,13 +85,13 @@ pub async fn restore_emails(account_id: u64, message_ids: Vec<u64>) -> BichonRes
|
||||
.await;
|
||||
|
||||
if let Err(err) = result {
|
||||
failed.push(message_id);
|
||||
tracing::warn!(
|
||||
account_id = account_id,
|
||||
message_id = message_id,
|
||||
message_id = &envelope_id,
|
||||
error = ?err,
|
||||
"Failed to restore email"
|
||||
);
|
||||
failed.push(envelope_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ use crate::modules::envelope::extractor::extract_envelope_from_message;
|
||||
use crate::modules::error::code::ErrorCode;
|
||||
use crate::modules::indexer::envelope::Envelope;
|
||||
use crate::modules::indexer::manager::{EML_INDEX_MANAGER, ENVELOPE_INDEX_MANAGER};
|
||||
use crate::modules::utils::create_hash;
|
||||
use crate::{modules::error::BichonResult, raise_error};
|
||||
use mail_parser::{MessageParser, MimeHeaders};
|
||||
|
||||
@@ -144,29 +143,32 @@ pub struct FullNestedMessageContent {
|
||||
pub envelope: Envelope,
|
||||
}
|
||||
|
||||
pub async fn retrieve_email_content(account_id: u64, id: u64) -> BichonResult<FullMessageContent> {
|
||||
pub async fn retrieve_email_content(
|
||||
account_id: u64,
|
||||
envelope_id: String,
|
||||
) -> BichonResult<FullMessageContent> {
|
||||
AccountModel::check_account_exists(account_id).await?;
|
||||
let envelope = ENVELOPE_INDEX_MANAGER
|
||||
.get_envelope_by_id(account_id, id)
|
||||
.get_envelope_by_id(account_id, envelope_id.clone())
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
raise_error!(
|
||||
format!(
|
||||
"Email record not found: account_id={} id={}",
|
||||
account_id, id
|
||||
account_id, &envelope_id
|
||||
),
|
||||
ErrorCode::ResourceNotFound
|
||||
)
|
||||
})?;
|
||||
let eml_id = create_hash(account_id, &envelope.message_id);
|
||||
|
||||
let eml = EML_INDEX_MANAGER
|
||||
.get(account_id, eml_id)
|
||||
.get(account_id, &envelope.content_hash)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
raise_error!(
|
||||
format!(
|
||||
"Email record not found: account_id={} id={}",
|
||||
account_id, id
|
||||
account_id, &envelope_id
|
||||
),
|
||||
ErrorCode::ResourceNotFound
|
||||
)
|
||||
@@ -175,7 +177,7 @@ pub async fn retrieve_email_content(account_id: u64, id: u64) -> BichonResult<Fu
|
||||
raise_error!(
|
||||
format!(
|
||||
"Failed to parse EML data (id={}) — the message may be corrupted.",
|
||||
id
|
||||
&envelope_id
|
||||
),
|
||||
ErrorCode::InternalError
|
||||
)
|
||||
@@ -186,14 +188,23 @@ pub async fn retrieve_email_content(account_id: u64, id: u64) -> BichonResult<Fu
|
||||
for attachment in message.attachments() {
|
||||
let content_type = attachment.content_type().ok_or_else(|| {
|
||||
raise_error!(
|
||||
format!("Attachment is missing Content-Type (email id={})", id),
|
||||
format!(
|
||||
"Attachment is missing Content-Type (email id={})",
|
||||
&envelope_id
|
||||
),
|
||||
ErrorCode::InternalError
|
||||
)
|
||||
})?;
|
||||
let filename = attachment
|
||||
.attachment_name()
|
||||
.map(|name| name.to_string())
|
||||
.unwrap_or_else(|| format!("email{}_attachment{}", id, attachment.raw_body_offset()));
|
||||
.unwrap_or_else(|| {
|
||||
format!(
|
||||
"email{}_attachment{}",
|
||||
&envelope_id,
|
||||
attachment.raw_body_offset()
|
||||
)
|
||||
});
|
||||
|
||||
let disposition = attachment.content_disposition();
|
||||
|
||||
@@ -242,7 +253,7 @@ pub async fn retrieve_email_content(account_id: u64, id: u64) -> BichonResult<Fu
|
||||
|
||||
pub async fn retrieve_nested_eml_content(
|
||||
account_id: u64,
|
||||
envelope_id: u64,
|
||||
envelope_id: String,
|
||||
name: &str,
|
||||
) -> BichonResult<FullNestedMessageContent> {
|
||||
let attachment_content = EML_INDEX_MANAGER
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::modules::error::BichonResult;
|
||||
use crate::modules::indexer::manager::{EML_INDEX_MANAGER, ENVELOPE_INDEX_MANAGER};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub async fn delete_messages_impl(request: HashMap<u64, Vec<u64>>) -> BichonResult<()> {
|
||||
pub async fn delete_messages_impl(request: HashMap<u64, Vec<String>>) -> BichonResult<()> {
|
||||
EML_INDEX_MANAGER
|
||||
.delete_email_multi_account(&request)
|
||||
.await?;
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
use crate::{
|
||||
modules::{
|
||||
account::migration::AccountModel,
|
||||
@@ -58,7 +57,7 @@ fn validate_pagination_params(page: u64, page_size: u64) -> BichonResult<()> {
|
||||
|
||||
pub async fn get_thread_messages(
|
||||
account_id: u64,
|
||||
thread_id: u64,
|
||||
thread_id: String,
|
||||
page: u64,
|
||||
page_size: u64,
|
||||
) -> BichonResult<DataPage<Envelope>> {
|
||||
|
||||
@@ -24,7 +24,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize, Object)]
|
||||
pub struct UpdateTagsRequest {
|
||||
pub updates: HashMap<u64, Vec<u64>>, // account_id -> envelope_ids
|
||||
pub updates: HashMap<u64, Vec<String>>, // account_id -> envelope_ids
|
||||
pub tags: Vec<String>,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user