mirror of
https://github.com/rustmailer/rustmailer.git
synced 2026-09-07 00:01:20 +00:00
feat: unify UID/MID and UIDs/MIDs into single id/ids fields
This commit is contained in:
+69
-79
@@ -566,35 +566,35 @@ service MailboxService {
|
||||
message MailboxTransferRequest {
|
||||
// The ID of the account.
|
||||
uint64 account_id = 1;
|
||||
// A list of unique identifiers (UIDs) of the messages to transfer (IMAP accounts only).
|
||||
// Ignored for Gmail API accounts.
|
||||
repeated uint32 uids = 2;
|
||||
// A list of Gmail API message IDs of the messages to transfer (Gmail API accounts only).
|
||||
// Ignored for IMAP accounts.
|
||||
repeated string mids = 3;
|
||||
// A list of unique message identifiers as strings.
|
||||
// - For IMAP accounts, each UID is converted to a numeric string (parseable back to `u32`).
|
||||
// - For Gmail API accounts, each element is a message ID (`mid`) returned by the API.
|
||||
// Unifying them as strings simplifies handling across different backends.
|
||||
repeated string ids = 2;
|
||||
// The name of the mailbox or label from which to transfer messages.
|
||||
// For IMAP: decoded human-readable mailbox name (e.g., "INBOX").
|
||||
// For Gmail API: the label name.
|
||||
string current_mailbox = 4;
|
||||
string current_mailbox = 3;
|
||||
// The name of the target mailbox or label to which messages will be transferred.
|
||||
// For IMAP: decoded human-readable mailbox name (e.g., "INBOX").
|
||||
// For Gmail API: the label name.
|
||||
string target_mailbox = 5;
|
||||
string target_mailbox = 4;
|
||||
}
|
||||
|
||||
// MessageDeleteRequest is used to delete messages from a mailbox.
|
||||
message MessageDeleteRequest {
|
||||
// The ID of the account.
|
||||
uint64 account_id = 1;
|
||||
/// A list of unique identifiers (UIDs) of the messages to be deleted (IMAP only).
|
||||
repeated uint32 uids = 2;
|
||||
/// A list of Gmail message IDs of the messages to be deleted (Gmail API only).
|
||||
repeated string mids = 3;
|
||||
// A list of unique message identifiers as strings.
|
||||
// - For IMAP accounts, each UID is converted to a numeric string (parseable back to `u32`).
|
||||
// - For Gmail API accounts, each element is a message ID (`mid`) returned by the API.
|
||||
// Unifying them as strings simplifies handling across different backends.
|
||||
repeated string ids = 2;
|
||||
/// The decoded, human-readable name of the mailbox containing the email (e.g., "INBOX"). (IMAP only)
|
||||
/// This name is presented as it appears to users, with any encoding (e.g., UTF-7) automatically handled by the system,
|
||||
/// so no manual decoding is required.
|
||||
/// In Gmail API, this field is not required and can be set to `None`.
|
||||
optional string mailbox_name = 4;
|
||||
optional string mailbox_name = 3;
|
||||
}
|
||||
|
||||
// FlagMessageRequest is used to update flags on messages within a mailbox.
|
||||
@@ -781,39 +781,37 @@ message FetchMessageContentRequest {
|
||||
uint64 account_id = 1;
|
||||
// The name of the mailbox containing the message.
|
||||
optional string mailbox_name = 2;
|
||||
// The unique identifier (UID) of the message.
|
||||
optional uint32 uid = 3;
|
||||
// The unique ID of the message, either IMAP UID or Gmail API MID.
|
||||
// - For IMAP accounts, this is the UID converted to a string. It must be a valid numeric string
|
||||
// that can be parsed back to a `u32`.
|
||||
// - For Gmail API accounts, this is the message ID (`mid`) returned by the API.
|
||||
string id = 3;
|
||||
// Optional: The maximum length of content to fetch.
|
||||
optional uint64 max_length = 4;
|
||||
// A list of specific body sections to fetch.
|
||||
repeated EmailBodyPart sections = 5;
|
||||
// A list of inline attachments to fetch.
|
||||
repeated ImapAttachment inline = 6;
|
||||
// The Gmail API message ID (Gmail API accounts only)
|
||||
// This is the `id` returned by `list messages` and used by `get message`.
|
||||
// For IMAP accounts, this field is ignored.
|
||||
optional string mid = 7;
|
||||
}
|
||||
|
||||
// FetchMessageAttachmentRequest is used to fetch a specific attachment from an email message.
|
||||
message FetchMessageAttachmentRequest {
|
||||
// The ID of the account.
|
||||
uint64 account_id = 1;
|
||||
// The unique identifier (UID) of the message.
|
||||
optional uint32 uid = 2;
|
||||
// The unique ID of the message, either IMAP UID or Gmail API MID.
|
||||
// - For IMAP accounts, this is the UID converted to a string. It must be a valid numeric string
|
||||
// that can be parsed back to a `u32`.
|
||||
// - For Gmail API accounts, this is the message ID (`mid`) returned by the API.
|
||||
string id = 2;
|
||||
// The name of the mailbox containing the message.
|
||||
optional string mailbox_name = 3;
|
||||
// The attachment to fetch, identified by its data.
|
||||
optional ImapAttachment attachment = 4;
|
||||
// The message identifier string (Gmail API `id`)
|
||||
// - Required for Gmail API accounts
|
||||
// - Not used for IMAP/SMTP
|
||||
optional string mid = 5;
|
||||
// Gmail API only: attachment info used to fetch it via Gmail API.
|
||||
optional AttachmentInfo attachment_info = 6;
|
||||
optional AttachmentInfo attachment_info = 5;
|
||||
// Optional: The filename of the attachment.
|
||||
// - Gmail API only.
|
||||
optional string filename = 7;
|
||||
optional string filename = 6;
|
||||
}
|
||||
|
||||
// FetchRawMessageRequest is used to fetch the complete raw content of an email message.
|
||||
@@ -822,10 +820,11 @@ message FetchRawMessageRequest {
|
||||
uint64 account_id = 1;
|
||||
// The name of the mailbox containing the message. (IMAP only)
|
||||
optional string mailbox_name = 2;
|
||||
// The unique identifier (UID) of the message. (IMAP only)
|
||||
optional uint32 uid = 3;
|
||||
// The Gmail message ID of the email to fetch. (Gmail API only)
|
||||
optional string mid = 4;
|
||||
// The unique ID of the message, either IMAP UID or Gmail API MID.
|
||||
// - For IMAP accounts, this is the UID converted to a string. It must be a valid numeric string
|
||||
// that can be parsed back to a `u32`.
|
||||
// - For Gmail API accounts, this is the message ID (`mid`) returned by the API.
|
||||
string id = 3;
|
||||
}
|
||||
|
||||
// MessageSearch represents a search query for email messages, which can be a single condition or a logical combination of conditions.
|
||||
@@ -1121,10 +1120,11 @@ message AppendReplyToDraftRequest {
|
||||
// - For IMAP accounts, this is the mailbox name where the source message resides.
|
||||
// - For Gmail API accounts, this refers to the label name associated with the source message.
|
||||
string mailbox_name = 2;
|
||||
// The UID of the message being replied to (IMAP accounts only)
|
||||
// For IMAP accounts, this identifies the specific message in the mailbox.
|
||||
// For Gmail API accounts, this field is ignored.
|
||||
optional uint32 uid = 3;
|
||||
// The unique ID of the message, either IMAP UID or Gmail API MID.
|
||||
// - For IMAP accounts, this is the UID converted to a string. It must be a valid numeric string
|
||||
// that can be parsed back to a `u32`.
|
||||
// - For Gmail API accounts, this is the message ID (`mid`) returned by the API.
|
||||
string id = 3;
|
||||
// Optional preview text for the reply email
|
||||
optional string preview = 4;
|
||||
// Optional plain text body of the reply email
|
||||
@@ -1135,10 +1135,6 @@ message AppendReplyToDraftRequest {
|
||||
// For example: "[Gmail]/Drafts".
|
||||
// For Gmail API accounts, this field is ignored.
|
||||
optional string draft_folder_path = 7;
|
||||
// The Gmail API message ID (Gmail API accounts only)
|
||||
// This is the `id` returned by `list messages` and used by `get message`.
|
||||
// For IMAP accounts, this field is ignored.
|
||||
optional string mid = 8;
|
||||
}
|
||||
|
||||
// MessageService provides APIs for interacting with email messages.
|
||||
@@ -1817,76 +1813,70 @@ message SendEmailRequest {
|
||||
message ReplyEmailRequest {
|
||||
// The name of the mailbox containing the original message.
|
||||
string mailbox_name = 1;
|
||||
// The UID of the message being replied to.
|
||||
// This identifies the specific message in the mailbox for **IMAP accounts**.
|
||||
// Should be `None` when using Gmail API accounts.
|
||||
optional uint32 uid = 2;
|
||||
// The message ID of the message being replied to.
|
||||
// This is used for **Gmail API accounts** instead of IMAP UID.
|
||||
// Should be `None` when using IMAP accounts.
|
||||
optional string mid = 3;
|
||||
// The unique ID of the message, either IMAP UID or Gmail API MID.
|
||||
// - For IMAP accounts, this is the UID converted to a string. It must be a valid numeric string
|
||||
// that can be parsed back to a `u32`.
|
||||
// - For Gmail API accounts, this is the message ID (`mid`) returned by the API.
|
||||
string id = 2;
|
||||
// Optional: The plain text content of the reply.
|
||||
optional string text = 4;
|
||||
optional string text = 3;
|
||||
// Optional: The HTML content of the reply.
|
||||
optional string html = 5;
|
||||
optional string html = 4;
|
||||
// Optional: A preview text for the reply.
|
||||
optional string preview = 6;
|
||||
optional string preview = 5;
|
||||
// A map of custom headers for the reply.
|
||||
map<string, HeaderValue> headers = 7;
|
||||
map<string, HeaderValue> headers = 6;
|
||||
// If true, replies to all recipients (To, CC) of the original email.
|
||||
bool reply_all = 8;
|
||||
bool reply_all = 7;
|
||||
// A list of attachments to include in the reply.
|
||||
repeated MailAttachment attachments = 9;
|
||||
repeated MailAttachment attachments = 8;
|
||||
// A list of additional CC recipients for the reply.
|
||||
repeated EmailAddress cc = 10;
|
||||
repeated EmailAddress cc = 9;
|
||||
// A list of additional BCC recipients for the reply.
|
||||
repeated EmailAddress bcc = 11;
|
||||
repeated EmailAddress bcc = 10;
|
||||
// Optional: The timezone for date headers in the reply.
|
||||
optional string timezone = 12;
|
||||
optional string timezone = 11;
|
||||
// If true, includes the original message content in the reply.
|
||||
bool include_original = 13;
|
||||
bool include_original = 12;
|
||||
// If true, includes all attachments from the original message in the reply.
|
||||
bool include_all_attachments = 14;
|
||||
bool include_all_attachments = 13;
|
||||
// Controls the sending process, including retry policies and DSN.
|
||||
SendControl send_control = 15;
|
||||
SendControl send_control = 14;
|
||||
}
|
||||
|
||||
// ForwardEmailRequest defines the parameters for forwarding an existing email.
|
||||
message ForwardEmailRequest {
|
||||
// The name of the mailbox containing the original message.
|
||||
string mailbox_name = 1;
|
||||
// The UID of the message being replied to.
|
||||
// This identifies the specific message in the mailbox for **IMAP accounts**.
|
||||
// Should be `None` when using Gmail API accounts.
|
||||
optional uint32 uid = 2;
|
||||
// The message ID of the message being replied to.
|
||||
// This is used for **Gmail API accounts** instead of IMAP UID.
|
||||
// Should be `None` when using IMAP accounts.
|
||||
optional string mid = 3;
|
||||
// The unique ID of the message, either IMAP UID or Gmail API MID.
|
||||
// - For IMAP accounts, this is the UID converted to a string. It must be a valid numeric string
|
||||
// that can be parsed back to a `u32`.
|
||||
// - For Gmail API accounts, this is the message ID (`mid`) returned by the API.
|
||||
string id = 2;
|
||||
// A list of primary recipients (To) for the forwarded email.
|
||||
repeated EmailAddress to = 4;
|
||||
repeated EmailAddress to = 3;
|
||||
// A list of carbon copy (CC) recipients for the forwarded email.
|
||||
repeated EmailAddress cc = 5;
|
||||
repeated EmailAddress cc = 4;
|
||||
// A list of blind carbon copy (BCC) recipients for the forwarded email.
|
||||
repeated EmailAddress bcc = 6;
|
||||
repeated EmailAddress bcc = 5;
|
||||
// Optional: The plain text content to add to the forwarded email.
|
||||
optional string text = 7;
|
||||
optional string text = 6;
|
||||
// Optional: The HTML content to add to the forwarded email.
|
||||
optional string html = 8;
|
||||
optional string html = 7;
|
||||
// Optional: A preview text for the forwarded email.
|
||||
optional string preview = 9;
|
||||
optional string preview = 8;
|
||||
// A map of custom headers for the forwarded email.
|
||||
map<string, HeaderValue> headers = 10;
|
||||
map<string, HeaderValue> headers = 9;
|
||||
// Optional: The timezone for date headers in the forwarded email.
|
||||
optional string timezone = 11;
|
||||
optional string timezone = 10;
|
||||
// A list of attachments to include in the forwarded email.
|
||||
repeated MailAttachment attachments = 12;
|
||||
repeated MailAttachment attachments = 11;
|
||||
// If true, includes the original message content in the forwarded email.
|
||||
bool include_original = 13;
|
||||
bool include_original = 12;
|
||||
// If true, includes all attachments from the original message in the forwarded email.
|
||||
bool include_all_attachments = 14;
|
||||
bool include_all_attachments = 13;
|
||||
// Controls the sending process, including retry policies and DSN.
|
||||
SendControl send_control = 15;
|
||||
SendControl send_control = 14;
|
||||
}
|
||||
|
||||
// EmailTask represents a single email sending task managed by the system.
|
||||
|
||||
@@ -246,7 +246,6 @@ fn parse_original_message_headers(message: &Message<'_>) -> Option<RawEmailHeade
|
||||
let part = rfc822_header_part?;
|
||||
|
||||
let sub_message = part.is_message().then(|| part.message()).flatten()?;
|
||||
|
||||
if sub_message.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Vendored
+1
-2
@@ -803,14 +803,13 @@ async fn process_email_added_events(
|
||||
Some(sections) => {
|
||||
let request = MessageContentRequest {
|
||||
mailbox: Some(remote.name.clone()),
|
||||
uid: Some(envelope.uid),
|
||||
id: envelope.uid.to_string(),
|
||||
max_length: Some(SETTINGS.rustmailer_max_email_content_length as usize),
|
||||
sections: Some(sections),
|
||||
inline: envelope
|
||||
.attachments
|
||||
.as_ref()
|
||||
.map(|att| att.iter().filter(|a| a.inline).cloned().collect()),
|
||||
mid: None,
|
||||
};
|
||||
retrieve_email_content(account.id, request, true).await?
|
||||
}
|
||||
|
||||
+13
-10
@@ -243,16 +243,13 @@ pub async fn apply_history(
|
||||
if !item.message.label_ids.contains(&label.label_id) {
|
||||
return None;
|
||||
}
|
||||
let message_data = match GmailClient::get_message(
|
||||
account_id,
|
||||
use_proxy.clone(),
|
||||
&item.message.id,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(msg) => msg,
|
||||
Err(_) => return None,
|
||||
};
|
||||
let message_data =
|
||||
match GmailClient::get_message(account_id, use_proxy.clone(), &item.message.id)
|
||||
.await
|
||||
{
|
||||
Ok(msg) => msg,
|
||||
Err(_) => return None,
|
||||
};
|
||||
if !message_data.label_ids.contains(&label.label_id) {
|
||||
return None;
|
||||
}
|
||||
@@ -277,6 +274,12 @@ pub async fn apply_history(
|
||||
}
|
||||
// save to local envelope cache and build some index
|
||||
if !messages_added.is_empty() {
|
||||
info!(
|
||||
"Gmail Api Account {} synced {} new messages in label '{}'",
|
||||
account.id,
|
||||
messages_added.len(),
|
||||
&label.name
|
||||
);
|
||||
GmailEnvelope::save_envelopes(messages_added.clone()).await?;
|
||||
if EventHookTask::is_watching_email_add_event(account.id).await? {
|
||||
dispatch_new_email_notification(account, messages_added).await?;
|
||||
|
||||
@@ -34,8 +34,8 @@ use crate::modules::{
|
||||
token::AccessToken,
|
||||
};
|
||||
|
||||
/// Metadata database instance
|
||||
pub struct DatabaseManager {
|
||||
/// Metadata database instance
|
||||
meta_db: Arc<Database<'static>>,
|
||||
/// Task scheduling database instance
|
||||
tasks_db: Arc<Database<'static>>,
|
||||
|
||||
@@ -29,10 +29,9 @@ use crate::modules::{
|
||||
impl From<rustmailer_grpc::MailboxTransferRequest> for MailboxTransferRequest {
|
||||
fn from(value: rustmailer_grpc::MailboxTransferRequest) -> Self {
|
||||
Self {
|
||||
uids: Some(value.uids).filter(|v| !v.is_empty()),
|
||||
ids: value.ids,
|
||||
current_mailbox: value.current_mailbox,
|
||||
target_mailbox: value.target_mailbox,
|
||||
mids: Some(value.mids).filter(|v| !v.is_empty()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,9 +39,8 @@ impl From<rustmailer_grpc::MailboxTransferRequest> for MailboxTransferRequest {
|
||||
impl From<rustmailer_grpc::MessageDeleteRequest> for MessageDeleteRequest {
|
||||
fn from(value: rustmailer_grpc::MessageDeleteRequest) -> Self {
|
||||
Self {
|
||||
uids: Some(value.uids).filter(|v| !v.is_empty()),
|
||||
ids: value.ids,
|
||||
mailbox: value.mailbox_name,
|
||||
mids: Some(value.mids).filter(|v| !v.is_empty()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -389,7 +387,7 @@ impl TryFrom<rustmailer_grpc::FetchMessageContentRequest> for MessageContentRequ
|
||||
fn try_from(value: rustmailer_grpc::FetchMessageContentRequest) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
mailbox: value.mailbox_name,
|
||||
uid: value.uid,
|
||||
id: value.id,
|
||||
max_length: value.max_length.map(|m| m as usize),
|
||||
sections: (!value.sections.is_empty())
|
||||
.then(|| {
|
||||
@@ -409,7 +407,6 @@ impl TryFrom<rustmailer_grpc::FetchMessageContentRequest> for MessageContentRequ
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
.transpose()?,
|
||||
mid: value.mid,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -473,10 +470,9 @@ impl TryFrom<rustmailer_grpc::FetchMessageAttachmentRequest> for AttachmentReque
|
||||
value: rustmailer_grpc::FetchMessageAttachmentRequest,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
uid: value.uid,
|
||||
id: value.id,
|
||||
mailbox: value.mailbox_name,
|
||||
attachment: value.attachment.map(|a| a.try_into()).transpose()?,
|
||||
mid: value.mid,
|
||||
attachment_info: value.attachment_info.map(|a| a.into()),
|
||||
filename: value.filename,
|
||||
})
|
||||
@@ -609,12 +605,11 @@ impl From<rustmailer_grpc::AppendReplyToDraftRequest> for AppendReplyToDraftRequ
|
||||
fn from(value: rustmailer_grpc::AppendReplyToDraftRequest) -> Self {
|
||||
Self {
|
||||
mailbox_name: value.mailbox_name,
|
||||
uid: value.uid,
|
||||
id: value.id,
|
||||
preview: value.preview,
|
||||
text: value.text,
|
||||
html: value.html,
|
||||
draft_folder_path: value.draft_folder_path,
|
||||
mid: value.mid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,13 +143,8 @@ impl MessageService for RustMailerMessageService {
|
||||
request: Request<FetchRawMessageRequest>,
|
||||
) -> Result<Response<ByteResponse>, Status> {
|
||||
let req = require_account_access(request, |r| r.account_id)?;
|
||||
let mut reader = retrieve_raw_email(
|
||||
req.account_id,
|
||||
req.mailbox_name.as_deref(),
|
||||
req.uid,
|
||||
req.mid.as_deref(),
|
||||
)
|
||||
.await?;
|
||||
let mut reader =
|
||||
retrieve_raw_email(req.account_id, req.mailbox_name.as_deref(), &req.id).await?;
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
reader
|
||||
|
||||
@@ -65,7 +65,7 @@ impl TryFrom<rustmailer_grpc::ReplyEmailRequest> for ReplyEmailRequest {
|
||||
fn try_from(value: rustmailer_grpc::ReplyEmailRequest) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
mailbox_name: value.mailbox_name,
|
||||
uid: value.uid,
|
||||
id: value.id,
|
||||
text: value.text,
|
||||
html: value.html,
|
||||
preview: value.preview,
|
||||
@@ -98,7 +98,6 @@ impl TryFrom<rustmailer_grpc::ReplyEmailRequest> for ReplyEmailRequest {
|
||||
include_original: value.include_original,
|
||||
include_all_attachments: value.include_all_attachments,
|
||||
send_control: { value.send_control.map(|c| c.try_into()).transpose()? },
|
||||
mid: value.mid,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -109,7 +108,7 @@ impl TryFrom<rustmailer_grpc::ForwardEmailRequest> for ForwardEmailRequest {
|
||||
fn try_from(value: rustmailer_grpc::ForwardEmailRequest) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
mailbox_name: value.mailbox_name,
|
||||
uid: value.uid,
|
||||
id: value.id,
|
||||
to: value.to.into_iter().map(Into::into).collect(),
|
||||
cc: (!value.cc.is_empty()).then(|| value.cc.into_iter().map(Into::into).collect()),
|
||||
bcc: (!value.bcc.is_empty()).then(|| value.bcc.into_iter().map(Into::into).collect()),
|
||||
@@ -141,7 +140,6 @@ impl TryFrom<rustmailer_grpc::ForwardEmailRequest> for ForwardEmailRequest {
|
||||
include_original: value.include_original,
|
||||
include_all_attachments: value.include_all_attachments,
|
||||
send_control: { value.send_control.map(|c| c.try_into()).transpose()? },
|
||||
mid: value.mid,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,12 +186,11 @@ async fn test6() {
|
||||
let request = AppendReplyToDraftRequest {
|
||||
account_id: 6637484689546669,
|
||||
mailbox_name: "INBOX".into(),
|
||||
uid: Some(395),
|
||||
id: "395".into(),
|
||||
preview: None,
|
||||
text: Some("hello world.".into()),
|
||||
html: None,
|
||||
draft_folder_path: Some("[Gmail]/Drafts".into()),
|
||||
mid: None,
|
||||
};
|
||||
|
||||
let mut request = poem_grpc::Request::new(request);
|
||||
@@ -247,12 +246,11 @@ async fn test8() {
|
||||
let request = AppendReplyToDraftRequest {
|
||||
account_id: 4391092875701825,
|
||||
mailbox_name: "INBOX".into(),
|
||||
uid: None,
|
||||
preview: None,
|
||||
text: Some("hello world.".into()),
|
||||
html: None,
|
||||
draft_folder_path: None,
|
||||
mid: Some("1970d297da3c2dd2".into()),
|
||||
id: "1970d297da3c2dd2".into(),
|
||||
};
|
||||
|
||||
let mut request = poem_grpc::Request::new(request);
|
||||
|
||||
@@ -37,16 +37,12 @@ pub struct AppendReplyToDraftRequest {
|
||||
/// - For Gmail API accounts, this refers to the label name associated with the source message.
|
||||
/// This is used to locate the message being replied to.
|
||||
pub mailbox_name: String,
|
||||
/// The UID of the message being replied to (IMAP accounts only).
|
||||
/// The unique ID of the message, either IMAP UID or Gmail API MID.
|
||||
///
|
||||
/// For IMAP accounts, this identifies the specific message in the mailbox.
|
||||
/// For Gmail API accounts, this field is ignored.
|
||||
pub uid: Option<u32>,
|
||||
/// The Gmail API message ID (Gmail API accounts only), sourced from [`EmailEnvelopeV3::mid`].
|
||||
///
|
||||
/// This is the `id` returned by `list messages` and used by `get message`.
|
||||
/// For IMAP accounts, this field is ignored.
|
||||
pub mid: Option<String>,
|
||||
/// - For IMAP accounts, this is the UID converted to a string. It must be a valid numeric string
|
||||
/// that can be parsed back to a `u32`.
|
||||
/// - For Gmail API accounts, this is the message ID (`mid`) returned by the API.
|
||||
pub id: String,
|
||||
/// A preview text for the reply email.
|
||||
///
|
||||
/// This optional field provides a short summary or preview of the reply content.
|
||||
@@ -79,19 +75,11 @@ impl AppendReplyToDraftRequest {
|
||||
));
|
||||
}
|
||||
|
||||
if is_gmail_api {
|
||||
// Gmail API account: mid required
|
||||
if self.mid.as_ref().map(|s| s.is_empty()).unwrap_or(true) {
|
||||
return Err(raise_error!(
|
||||
"mid is required for Gmail API accounts".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if !is_gmail_api {
|
||||
// IMAP account: uid and draft_folder_path required
|
||||
if self.uid.is_none() {
|
||||
if self.id.parse::<u32>().is_err() {
|
||||
return Err(raise_error!(
|
||||
"uid is required for IMAP accounts".into(),
|
||||
"Invalid IMAP UID: `id` must be a numeric string".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
@@ -129,12 +117,7 @@ impl AppendReplyToDraftRequest {
|
||||
let envelope = EmailHandler::get_envelope(
|
||||
account,
|
||||
&self.mailbox_name,
|
||||
self.uid.ok_or_else(|| {
|
||||
raise_error!(
|
||||
"uid is missing but required for IMAP accounts".into(),
|
||||
ErrorCode::InternalError
|
||||
)
|
||||
})?,
|
||||
self.id.parse::<u32>().ok().unwrap(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -187,28 +170,17 @@ impl AppendReplyToDraftRequest {
|
||||
account_id: u64,
|
||||
) -> RustMailerResult<()> {
|
||||
let target_label = GmailLabels::get_by_name(account_id, &self.mailbox_name).await?;
|
||||
let envelope = GmailEnvelope::find(
|
||||
account_id,
|
||||
target_label.id,
|
||||
&self.mid.as_ref().ok_or_else(|| {
|
||||
let envelope = GmailEnvelope::find(account_id, target_label.id, &self.id)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
raise_error!(
|
||||
"mid is missing but required for Gmail API accounts".into(),
|
||||
ErrorCode::InternalError
|
||||
format!(
|
||||
"Gmail message with id '{}' not found in label '{}' for account {}",
|
||||
self.id, target_label.name, account_id
|
||||
),
|
||||
ErrorCode::ResourceNotFound
|
||||
)
|
||||
})?,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
raise_error!(
|
||||
format!(
|
||||
"Gmail message with id '{}' not found in label '{}' for account {}",
|
||||
self.mid.as_ref().unwrap(),
|
||||
target_label.name,
|
||||
account_id
|
||||
),
|
||||
ErrorCode::ResourceNotFound
|
||||
)
|
||||
})?;
|
||||
})?;
|
||||
|
||||
let from = Address::new_address(
|
||||
account.name.as_ref().map(|n| Cow::Owned(n.to_string())),
|
||||
|
||||
@@ -26,19 +26,18 @@ const MAX_ATTACHMENT_SIZE: usize = 52_428_800; // 50MB
|
||||
/// Represents a request to fetch an attachment from a message in a mailbox.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize, Object)]
|
||||
pub struct AttachmentRequest {
|
||||
/// IMAP only: The UID of the message containing the attachment.
|
||||
/// Not used for Gmail API accounts.
|
||||
pub uid: Option<u32>,
|
||||
/// IMAP only: The name of the mailbox where the message is located.
|
||||
/// Not used for Gmail API accounts.
|
||||
pub mailbox: Option<String>,
|
||||
/// IMAP only: The metadata describing the attachment to fetch.
|
||||
/// Not used for Gmail API accounts.
|
||||
pub attachment: Option<ImapAttachment>,
|
||||
/// The message identifier string (Gmail API `id`)
|
||||
/// - Required for Gmail API accounts
|
||||
/// - Not used for IMAP/SMTP
|
||||
pub mid: Option<String>,
|
||||
/// The unique ID of the message, either IMAP UID or Gmail API MID.
|
||||
///
|
||||
/// - For IMAP accounts, this is the UID converted to a string. It must be a valid numeric string
|
||||
/// that can be parsed back to a `u32`.
|
||||
/// - For Gmail API accounts, this is the message ID (`mid`) returned by the API.
|
||||
pub id: String,
|
||||
/// Gmail API only: attachment info used to fetch it via Gmail API.
|
||||
/// Not used for IMAP accounts.
|
||||
pub attachment_info: Option<AttachmentInfo>,
|
||||
@@ -51,13 +50,19 @@ impl AttachmentRequest {
|
||||
pub fn validate(&self, account: &AccountV2) -> RustMailerResult<()> {
|
||||
match account.mailer_type {
|
||||
MailerType::ImapSmtp => {
|
||||
if self.uid.is_none() || self.mailbox.is_none() || self.attachment.is_none() {
|
||||
if self.mailbox.is_none() || self.attachment.is_none() {
|
||||
return Err(raise_error!(
|
||||
format!(
|
||||
"Current account type is `ImapSmtp`. Downloading attachments requires `uid`, `mailbox`, and `attachment` metadata."
|
||||
),
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
format!(
|
||||
"Current account type is `ImapSmtp`. Downloading attachments requires `uid`, `mailbox`, and `attachment` metadata."
|
||||
),
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
if self.id.parse::<u32>().is_err() {
|
||||
return Err(raise_error!(
|
||||
"Invalid IMAP UID: `id` must be a numeric string".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
}
|
||||
MailerType::GmailApi => {
|
||||
@@ -67,12 +72,6 @@ impl AttachmentRequest {
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
if self.mid.is_none() {
|
||||
return Err(raise_error!(
|
||||
"Current account type is `Gmail API`. Downloading attachments requires `mid`.".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -151,9 +150,9 @@ pub async fn retrieve_email_attachment(
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
let uid = request.uid.ok_or_else(|| {
|
||||
let uid = request.id.parse::<u32>().ok().ok_or_else(|| {
|
||||
raise_error!(
|
||||
"`uid` is required when retrieving attachments for IMAP/SMTP accounts.".into(),
|
||||
"Invalid IMAP UID: `id` must be a numeric string".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
@@ -162,13 +161,6 @@ pub async fn retrieve_email_attachment(
|
||||
Ok((reader, filename))
|
||||
}
|
||||
MailerType::GmailApi => {
|
||||
let mid = request.mid.as_ref().ok_or_else(|| {
|
||||
raise_error!(
|
||||
"`mid` is required when retrieving attachments for Gmail API accounts.".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
|
||||
let attachment_info = request.attachment_info.as_ref().ok_or_else(|| {
|
||||
raise_error!(
|
||||
"`attachment_info` is required when retrieving attachments for Gmail API accounts.".into(),
|
||||
@@ -176,7 +168,7 @@ pub async fn retrieve_email_attachment(
|
||||
)
|
||||
})?;
|
||||
let filename = request.filename;
|
||||
let reader = retrieve_gmail_attachment(&account, &mid, &attachment_info).await?;
|
||||
let reader = retrieve_gmail_attachment(&account, &request.id, &attachment_info).await?;
|
||||
Ok((reader, filename))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,14 +36,12 @@ pub struct MessageContentRequest {
|
||||
/// - Required for IMAP/SMTP accounts
|
||||
/// - Not used for Gmail API
|
||||
pub mailbox: Option<String>,
|
||||
/// The unique identifier of the message within the mailbox (IMAP UID)
|
||||
/// - Required for IMAP/SMTP accounts
|
||||
/// - Not used for Gmail API
|
||||
pub uid: Option<u32>,
|
||||
/// The message identifier string (Gmail API `id`)
|
||||
/// - Required for Gmail API accounts
|
||||
/// - Not used for IMAP/SMTP
|
||||
pub mid: Option<String>,
|
||||
/// The unique ID of the message, either IMAP UID or Gmail API MID.
|
||||
///
|
||||
/// - For IMAP accounts, this is the UID converted to a string. It must be a valid numeric string
|
||||
/// that can be parsed back to a `u32`.
|
||||
/// - For Gmail API accounts, this is the message ID (`mid`) returned by the API.
|
||||
pub id: String,
|
||||
/// Optional maximum length to retrieve for text parts (useful for large messages)
|
||||
/// - Supported by both IMAP/SMTP and Gmail API
|
||||
pub max_length: Option<usize>,
|
||||
@@ -67,9 +65,9 @@ impl MessageContentRequest {
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
if self.uid.is_none() {
|
||||
if self.id.parse::<u32>().is_err() {
|
||||
return Err(raise_error!(
|
||||
"`uid` is required for IMAP/SMTP accounts.".into(),
|
||||
"Invalid IMAP UID: `id` must be a numeric string".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
@@ -79,32 +77,14 @@ impl MessageContentRequest {
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
if self.mid.is_some() {
|
||||
return Err(raise_error!(
|
||||
"`mid` must not be set for IMAP/SMTP accounts.".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
}
|
||||
MailerType::GmailApi => {
|
||||
if self.mid.is_none() {
|
||||
return Err(raise_error!(
|
||||
"`mid` is required for Gmail API accounts.".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
if self.mailbox.is_some() {
|
||||
return Err(raise_error!(
|
||||
"`mailbox` must not be set for Gmail API accounts.".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
if self.uid.is_some() {
|
||||
return Err(raise_error!(
|
||||
"`uid` must not be set for Gmail API accounts.".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
if self.sections.is_some() {
|
||||
return Err(raise_error!(
|
||||
"`sections` is only supported for IMAP/SMTP accounts.".into(),
|
||||
@@ -415,9 +395,9 @@ pub async fn retrieve_email_content(
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
let uid = request.uid.ok_or_else(|| {
|
||||
let uid = request.id.parse::<u32>().ok().ok_or_else(|| {
|
||||
raise_error!(
|
||||
"`uid` is required when retrieving IMAP/SMTP message content.".into(),
|
||||
"Invalid IMAP UID: `id` must be a numeric string".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
@@ -440,18 +420,8 @@ pub async fn retrieve_email_content(
|
||||
.await
|
||||
}
|
||||
MailerType::GmailApi => {
|
||||
retrieve_gmail_message_content(
|
||||
account_id,
|
||||
request.mid.ok_or_else(|| {
|
||||
raise_error!(
|
||||
"`mid` is required when retrieving Gmail API message content.".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?,
|
||||
request.max_length,
|
||||
skip_cache,
|
||||
)
|
||||
.await
|
||||
retrieve_gmail_message_content(account_id, request.id, request.max_length, skip_cache)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,12 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize, Object)]
|
||||
pub struct MessageDeleteRequest {
|
||||
/// A list of unique identifiers (UIDs) of the messages to be deleted (IMAP only).
|
||||
pub uids: Option<Vec<u32>>,
|
||||
/// A list of Gmail message IDs of the messages to be deleted (Gmail API only).
|
||||
pub mids: Option<Vec<String>>,
|
||||
/// A list of unique message identifiers as strings.
|
||||
///
|
||||
/// - For IMAP accounts, each UID is converted to a numeric string (parseable back to `u32`).
|
||||
/// - For Gmail API accounts, each element is a message ID (`mid`) returned by the API.
|
||||
/// Unifying them as strings simplifies handling across different backends.
|
||||
pub ids: Vec<String>,
|
||||
/// The decoded, human-readable name of the mailbox containing the email (e.g., "INBOX"). (IMAP only)
|
||||
/// This name is presented as it appears to users, with any encoding (e.g., UTF-7) automatically handled by the system,
|
||||
/// so no manual decoding is required.
|
||||
@@ -41,24 +43,29 @@ pub async fn move_to_trash(
|
||||
)
|
||||
})?;
|
||||
|
||||
let uids = request.uids.as_deref().ok_or_else(|| {
|
||||
raise_error!(
|
||||
"IMAP request missing required field 'uids'".into(),
|
||||
if request.ids.is_empty() {
|
||||
return Err(raise_error!(
|
||||
"`ids` must contain at least one element".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
));
|
||||
}
|
||||
|
||||
move_to_trash_or_delete_messages_directly(account_id, uids, mailbox).await
|
||||
}
|
||||
MailerType::GmailApi => {
|
||||
let mids = request.mids.as_deref().ok_or_else(|| {
|
||||
raise_error!(
|
||||
"Gmail request missing required field 'mids'".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
gmail_move_to_trash(&account, mids).await
|
||||
let uids: Vec<u32> = request
|
||||
.ids
|
||||
.iter()
|
||||
.map(|id| {
|
||||
id.parse::<u32>().map_err(|_| {
|
||||
raise_error!(
|
||||
format!("Invalid IMAP UID: '{}', must be a numeric string", id),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
move_to_trash_or_delete_messages_directly(account_id, &uids, mailbox).await
|
||||
}
|
||||
MailerType::GmailApi => gmail_move_to_trash(&account, &request.ids).await,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,7 @@ fn gmail_raw_email_diskcache_key(account_id: u64, mid: &str) -> String {
|
||||
pub async fn retrieve_raw_email(
|
||||
account_id: u64,
|
||||
mailbox: Option<&str>,
|
||||
uid: Option<u32>,
|
||||
mid: Option<&str>,
|
||||
id: &str,
|
||||
) -> RustMailerResult<cacache::Reader> {
|
||||
let account = AccountV2::check_account_active(account_id, false).await?;
|
||||
match account.mailer_type {
|
||||
@@ -50,23 +49,15 @@ pub async fn retrieve_raw_email(
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
let uid = uid.ok_or_else(|| {
|
||||
let uid = id.parse::<u32>().ok().ok_or_else(|| {
|
||||
raise_error!(
|
||||
"Missing required parameter: `uid` for IMAP/SMTP".into(),
|
||||
"Invalid IMAP UID: `id` must be a numeric string".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
retrieve_imap_raw_email(account_id, mailbox, uid).await
|
||||
}
|
||||
MailerType::GmailApi => {
|
||||
let mid = mid.ok_or_else(|| {
|
||||
raise_error!(
|
||||
"Missing required parameter: `mid` for Gmail API".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
retrieve_gmail_raw_email(&account, mid).await
|
||||
}
|
||||
MailerType::GmailApi => retrieve_gmail_raw_email(&account, id).await,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,12 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize, Object)]
|
||||
pub struct MailboxTransferRequest {
|
||||
/// A list of unique identifiers (UIDs) for the messages to be moved (IMAP accounts only).
|
||||
/// For Gmail API accounts, this field is ignored.
|
||||
pub uids: Option<Vec<u32>>,
|
||||
/// A list of Gmail API message IDs for the messages to be moved (Gmail API accounts only).
|
||||
/// For IMAP accounts, this field is ignored.
|
||||
pub mids: Option<Vec<String>>,
|
||||
/// A list of unique message identifiers as strings.
|
||||
///
|
||||
/// - For IMAP accounts, each UID is converted to a numeric string (parseable back to `u32`).
|
||||
/// - For Gmail API accounts, each element is a message ID (`mid`) returned by the API.
|
||||
/// Unifying them as strings simplifies handling across different backends.
|
||||
pub ids: Vec<String>,
|
||||
/// The name of the mailbox from which the messages will be moved.
|
||||
/// For IMAP: the decoded, human-readable name of the mailbox (e.g., "INBOX").
|
||||
/// For Gmail API: represents the label name.
|
||||
@@ -51,19 +51,27 @@ pub async fn transfer_messages(
|
||||
|
||||
match account.mailer_type {
|
||||
MailerType::ImapSmtp => {
|
||||
let uids = payload.uids.as_ref().ok_or_else(|| {
|
||||
raise_error!(
|
||||
"IMAP copy requires `uids`, but none were provided".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
if uids.is_empty() {
|
||||
if payload.ids.is_empty() {
|
||||
return Err(raise_error!(
|
||||
"IMAP copy requires at least one UID".into(),
|
||||
"`ids` must contain at least one element".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
let uid_set = generate_uid_set(uids.clone());
|
||||
|
||||
let uids: Vec<u32> = payload
|
||||
.ids
|
||||
.iter()
|
||||
.map(|id| {
|
||||
id.parse::<u32>().map_err(|_| {
|
||||
raise_error!(
|
||||
format!("Invalid IMAP UID: '{}', must be a numeric string", id),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let uid_set = generate_uid_set(uids);
|
||||
let executor = RUST_MAIL_CONTEXT.imap(account_id).await?;
|
||||
// Encode the mailbox names using UTF-7 encoding
|
||||
let current_mailbox = encode_mailbox_name!(payload.current_mailbox.clone());
|
||||
@@ -93,12 +101,7 @@ pub async fn transfer_messages(
|
||||
}
|
||||
}
|
||||
MailerType::GmailApi => {
|
||||
let mids = payload.mids.as_ref().ok_or_else(|| {
|
||||
raise_error!(
|
||||
"Gmail API copy requires `mids`, but none were provided".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
let mids = &payload.ids;
|
||||
|
||||
if mids.is_empty() {
|
||||
return Err(raise_error!(
|
||||
@@ -107,10 +110,10 @@ pub async fn transfer_messages(
|
||||
));
|
||||
}
|
||||
|
||||
if mids.len() > 1000 {
|
||||
if mids.len() > 500 {
|
||||
return Err(raise_error!(
|
||||
format!(
|
||||
"Gmail API batchModify supports at most 1000 message IDs, got {}",
|
||||
"Gmail API batchModify supports at most 500 message IDs, got {}",
|
||||
mids.len()
|
||||
),
|
||||
ErrorCode::InvalidParameter
|
||||
|
||||
@@ -269,10 +269,11 @@ impl MessageApi {
|
||||
/// This name is presented as it appears to users, with any encoding (e.g., UTF-7) automatically handled by the system,
|
||||
/// so no manual decoding is required.
|
||||
mailbox: Query<Option<String>>,
|
||||
/// The IMAP UID of the email to fetch.
|
||||
uid: Query<Option<u32>>,
|
||||
/// The Gmail message ID of the email to fetch.
|
||||
mid: Query<Option<String>>,
|
||||
/// The unique ID of the message, either IMAP UID or Gmail API MID.
|
||||
/// - For IMAP accounts, this is the UID converted to a string. It must be a valid numeric string
|
||||
/// that can be parsed back to a `u32`.
|
||||
/// - For Gmail API accounts, this is the message ID (`mid`) returned by the API.
|
||||
id: Query<String>,
|
||||
/// An optional filename for the attachment (defaults to a timestamped `.elm` file).
|
||||
filename: Query<Option<String>>,
|
||||
context: ClientContext,
|
||||
@@ -281,13 +282,12 @@ impl MessageApi {
|
||||
context.require_account_access(account_id)?;
|
||||
let filename = filename.0.unwrap_or(format!("{}.elm", current_datetime!()));
|
||||
let mailbox_opt = mailbox.0.as_ref().map(|m| m.trim().to_owned());
|
||||
let mid_opt = mid.0.as_ref().map(|m| m.trim().to_owned());
|
||||
let id = id.0.trim();
|
||||
|
||||
let reader = retrieve_raw_email(
|
||||
account_id,
|
||||
mailbox_opt.as_deref(),
|
||||
uid.0,
|
||||
mid_opt.as_deref(),
|
||||
id
|
||||
)
|
||||
.await?;
|
||||
let body = Body::from_async_read(reader);
|
||||
|
||||
@@ -36,16 +36,12 @@ pub struct ForwardEmailRequest {
|
||||
///
|
||||
/// This is used to locate the source message that is being forwarded.
|
||||
pub mailbox_name: String,
|
||||
/// The UID of the message being forwarded to.
|
||||
/// The unique ID of the message, either IMAP UID or Gmail API MID.
|
||||
///
|
||||
/// This identifies the specific message in the mailbox for **IMAP accounts**.
|
||||
/// Should be `None` when using Gmail API accounts.
|
||||
pub uid: Option<u32>,
|
||||
/// The message ID of the message being forwarded to.
|
||||
///
|
||||
/// This is used for **Gmail API accounts** instead of IMAP UID.
|
||||
/// Should be `None` when using IMAP accounts.
|
||||
pub mid: Option<String>,
|
||||
/// - For IMAP accounts, this is the UID converted to a string. It must be a valid numeric string
|
||||
/// that can be parsed back to a `u32`.
|
||||
/// - For Gmail API accounts, this is the message ID (`mid`) returned by the API.
|
||||
pub id: String,
|
||||
/// The list of primary recipients to forward the email to.
|
||||
///
|
||||
/// At least one recipient must be specified.
|
||||
@@ -163,9 +159,9 @@ impl EmailBuilder for ForwardEmailRequest {
|
||||
|
||||
let (envelope, answer_email) = match account.mailer_type {
|
||||
MailerType::ImapSmtp => {
|
||||
let uid = self.uid.ok_or_else(|| {
|
||||
let uid = self.id.parse::<u32>().ok().ok_or_else(|| {
|
||||
raise_error!(
|
||||
"Missing required field `uid` for IMAP account".into(),
|
||||
"Invalid IMAP UID: `id` must be a numeric string".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
@@ -180,14 +176,8 @@ impl EmailBuilder for ForwardEmailRequest {
|
||||
)
|
||||
}
|
||||
MailerType::GmailApi => {
|
||||
let mid = self.mid.as_ref().ok_or_else(|| {
|
||||
raise_error!(
|
||||
"Missing required field `mid` for Gmail API account".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
let envelope =
|
||||
EmailHandler::get_gmail_envelope(account, &self.mailbox_name, mid).await?;
|
||||
EmailHandler::get_gmail_envelope(account, &self.mailbox_name, &self.id).await?;
|
||||
(envelope, None)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -212,10 +212,9 @@ impl MailAttachment {
|
||||
let (mut reader, _) = retrieve_email_attachment(
|
||||
account.id,
|
||||
AttachmentRequest {
|
||||
uid: Some(attachment_ref.uid),
|
||||
id: attachment_ref.uid.to_string(),
|
||||
mailbox: Some(attachment_ref.mailbox_name.clone()),
|
||||
attachment: Some(attachment_ref.attachment_data.clone()),
|
||||
mid: None,
|
||||
attachment_info: None,
|
||||
filename: None,
|
||||
},
|
||||
@@ -621,11 +620,10 @@ impl EmailHandler {
|
||||
});
|
||||
let request = MessageContentRequest {
|
||||
mailbox: Some(envelope.mailbox_name.clone()),
|
||||
uid: Some(envelope.uid),
|
||||
id: envelope.uid.to_string(),
|
||||
max_length: None,
|
||||
sections: Some(body_meta.clone()),
|
||||
inline: inline_attachments,
|
||||
mid: None,
|
||||
};
|
||||
retrieve_email_content(account.id, request, false)
|
||||
.await
|
||||
|
||||
@@ -32,16 +32,12 @@ pub struct ReplyEmailRequest {
|
||||
///
|
||||
/// This is used to locate the source message that is being replied to.
|
||||
pub mailbox_name: String,
|
||||
/// The UID of the message being replied to.
|
||||
/// The unique ID of the message, either IMAP UID or Gmail API MID.
|
||||
///
|
||||
/// This identifies the specific message in the mailbox for **IMAP accounts**.
|
||||
/// Should be `None` when using Gmail API accounts.
|
||||
pub uid: Option<u32>,
|
||||
/// The message ID of the message being replied to.
|
||||
///
|
||||
/// This is used for **Gmail API accounts** instead of IMAP UID.
|
||||
/// Should be `None` when using IMAP accounts.
|
||||
pub mid: Option<String>,
|
||||
/// - For IMAP accounts, this is the UID converted to a string. It must be a valid numeric string
|
||||
/// that can be parsed back to a `u32`.
|
||||
/// - For Gmail API accounts, this is the message ID (`mid`) returned by the API.
|
||||
pub id: String,
|
||||
/// The plain text body of the reply email.
|
||||
///
|
||||
/// This field is optional and can be used to provide plain text content.
|
||||
@@ -140,9 +136,9 @@ impl EmailBuilder for ReplyEmailRequest {
|
||||
|
||||
let (envelope, answer_email) = match account.mailer_type {
|
||||
MailerType::ImapSmtp => {
|
||||
let uid = self.uid.ok_or_else(|| {
|
||||
let uid = self.id.parse::<u32>().ok().ok_or_else(|| {
|
||||
raise_error!(
|
||||
"Missing required field `uid` for IMAP account".into(),
|
||||
"Invalid IMAP UID: `id` must be a numeric string".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
@@ -157,14 +153,8 @@ impl EmailBuilder for ReplyEmailRequest {
|
||||
)
|
||||
}
|
||||
MailerType::GmailApi => {
|
||||
let mid = self.mid.as_ref().ok_or_else(|| {
|
||||
raise_error!(
|
||||
"Missing required field `mid` for Gmail API account".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
)
|
||||
})?;
|
||||
let envelope =
|
||||
EmailHandler::get_gmail_envelope(account, &self.mailbox_name, mid).await?;
|
||||
EmailHandler::get_gmail_envelope(account, &self.mailbox_name, &self.id).await?;
|
||||
(envelope, None)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -163,9 +163,9 @@ export function MailDisplayDrawer({ open, setOpen, onOpenChange, currentEnvelope
|
||||
if (currentEnvelope.mid) {
|
||||
setLoading(true);
|
||||
let payload: {
|
||||
mid: string;
|
||||
id: string;
|
||||
} = {
|
||||
mid: currentEnvelope.mid
|
||||
id: currentEnvelope.mid
|
||||
};
|
||||
loadMessageMutation.mutate({ accountId: currentAccountId!, payload })
|
||||
} else {
|
||||
@@ -191,12 +191,12 @@ export function MailDisplayDrawer({ open, setOpen, onOpenChange, currentEnvelope
|
||||
inlineAttachments = currentEnvelope.attachments.filter(attachment => attachment.inline === true);
|
||||
}
|
||||
let payload: {
|
||||
uid: number;
|
||||
id: string;
|
||||
mailbox: string | undefined;
|
||||
sections: EmailBodyPart[];
|
||||
inline?: Attachment[]; // Declare inline as an optional field
|
||||
} = {
|
||||
uid: currentEnvelope.uid,
|
||||
id: currentEnvelope.uid.toString(),
|
||||
mailbox: currentMailbox?.name,
|
||||
sections: [emailbody],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user