This commit is contained in:
Mateusz Gruszczyński committed 2026-10-31 14:36:07 +01:00
1 parent fc81ecafd6
commit dc57aa7089
89 files changed
+5290 -1551

No files matched your search

+78 -58
View File
@@ -402,7 +402,9 @@ fn upsert_and_index_headers(
}
})
.collect();
let _ = user_index.index_messages_batch(&indexable);
user_index
.index_messages_batch_with_connection(conn, &indexable)
.map_err(|e| AppError::InternalError(format!("Search outbox error: {e}")))?;
}
Ok(())
@@ -545,7 +547,6 @@ pub async fn get_message(
.map_err(|e| AppError::InternalError(format!("Database error: {e}")))?;
let usable_cache = cached_body.filter(|c| c.attachments_json.is_some());
let fetched_body = usable_cache.is_none();
let (body_html, body_text, attachments, raw_headers, email_theme) = if let Some(cached) =
usable_cache
@@ -585,18 +586,32 @@ pub async fn get_message(
.await
.map_err(|e| match e {
crate::imap::client::ImapError::MessageNotFound { .. } => {
let _ = db::messages::delete_message(&conn, &folder, uid);
let _ = db::folders::invalidate_folder_freshness(&conn, &folder);
if let Ok(user_index) = search_engine.open_user_index(&session.user_hash) {
let _ = user_index.delete_message(uid, &folder);
if let Ok(transaction) = conn.unchecked_transaction() {
let _ = db::messages::delete_message(&transaction, &folder, uid);
let _ = db::folders::invalidate_folder_freshness(&transaction, &folder);
if let Ok(user_index) = search_engine.open_user_index(&session.user_hash) {
let _ = user_index.delete_message_with_connection(
&transaction,
uid,
&folder,
);
}
let _ = transaction.commit();
}
AppError::NotFound(format!("Message UID {uid} not found in folder {folder}"))
}
other => {
let _ = db::messages::delete_message(&conn, &folder, uid);
let _ = db::folders::invalidate_folder_freshness(&conn, &folder);
if let Ok(user_index) = search_engine.open_user_index(&session.user_hash) {
let _ = user_index.delete_message(uid, &folder);
if let Ok(transaction) = conn.unchecked_transaction() {
let _ = db::messages::delete_message(&transaction, &folder, uid);
let _ = db::folders::invalidate_folder_freshness(&transaction, &folder);
if let Ok(user_index) = search_engine.open_user_index(&session.user_hash) {
let _ = user_index.delete_message_with_connection(
&transaction,
uid,
&folder,
);
}
let _ = transaction.commit();
}
AppError::ServiceUnavailable(format!("IMAP error: {other}"))
}
@@ -639,8 +654,11 @@ pub async fn get_message(
.and_then(|h| email_theme::detect_email_theme(h))
.map(|t| t.as_i32());
let transaction = conn
.unchecked_transaction()
.map_err(|e| AppError::InternalError(format!("Database transaction error: {e}")))?;
db::messages::cache_message_body(
&conn,
&transaction,
&folder,
uid,
resolved_html.as_deref(),
@@ -651,6 +669,31 @@ pub async fn get_message(
)
.map_err(|e| AppError::InternalError(format!("Database error: {e}")))?;
if let Some(text) = body.text_plain.as_ref()
&& !UserIndex::is_excluded_folder(&folder)
&& let Some(msg) = db::messages::get_single_message(&transaction, &folder, uid)
.map_err(|e| AppError::InternalError(format!("Database error: {e}")))?
&& let Ok(user_index) = search_engine.open_user_index(&session.user_hash)
{
let indexable = IndexableMessage {
uid: msg.uid,
folder: msg.folder,
subject: msg.subject,
from_address: msg.from_address,
from_name: msg.from_name,
to_addresses: msg.to_addresses,
body_text: text.clone(),
date_epoch: crate::db::messages::parse_date_to_epoch_public(&msg.date),
has_attachments: msg.has_attachments,
};
user_index
.index_message_with_connection(&transaction, &indexable)
.map_err(|e| AppError::InternalError(format!("Search outbox error: {e}")))?;
}
transaction
.commit()
.map_err(|e| AppError::InternalError(format!("Database commit error: {e}")))?;
(
resolved_html,
body.text_plain,
@@ -742,30 +785,6 @@ pub async fn get_message(
}
};
if fetched_body
&& let Some(ref text) = body_text
&& !UserIndex::is_excluded_folder(&folder)
{
let indexable = IndexableMessage {
uid: msg.uid,
folder: msg.folder.clone(),
subject: msg.subject.clone(),
from_address: msg.from_address.clone(),
from_name: msg.from_name.clone(),
to_addresses: msg.to_addresses.clone(),
body_text: text.clone(),
date_epoch: crate::db::messages::parse_date_to_epoch_public(&msg.date),
has_attachments: msg.has_attachments,
};
let search_engine = Arc::clone(&search_engine);
let user_hash = session.user_hash.clone();
tokio::task::spawn_blocking(move || {
if let Ok(user_index) = search_engine.open_user_index(&user_hash) {
let _ = user_index.index_message(&indexable);
}
});
}
let thread_messages = if let Some(ref message_id) = msg.message_id {
db::messages::get_full_thread(&conn, message_id, msg.references_header.as_deref())
.unwrap_or_default()
@@ -973,34 +992,31 @@ pub async fn move_message_handler(
let conn = db::pool::open_user_db(&config.data_dir, &session.user_hash)
.map_err(|e| AppError::InternalError(format!("Database error: {e}")))?;
db::messages::remove_after_mutation(&conn, &body.from_folder, body.uid)
let transaction = conn
.unchecked_transaction()
.map_err(|e| AppError::InternalError(format!("Database transaction error: {e}")))?;
db::messages::remove_after_mutation_in_transaction(&transaction, &body.from_folder, body.uid)
.map_err(|e| AppError::InternalError(format!("Database error: {e}")))?;
let user_hash = session.user_hash.clone();
let from_folder = body.from_folder.clone();
let uid = body.uid;
let search_result = tokio::task::spawn_blocking(move || {
let index = search_engine.open_user_index(&user_hash)?;
index.delete_message(uid, &from_folder)
})
.await;
if !matches!(search_result, Ok(Ok(()))) {
tracing::warn!(
?search_result,
"Failed to remove moved message from search index"
);
if let Ok(index) = search_engine.open_user_index(&session.user_hash) {
index
.delete_message_with_connection(&transaction, body.uid, &body.from_folder)
.map_err(|e| AppError::InternalError(format!("Search outbox error: {e}")))?;
}
db::folders::refresh_unread_count(&conn, &body.from_folder)
db::folders::refresh_unread_count(&transaction, &body.from_folder)
.map_err(|e| AppError::InternalError(format!("Database error: {e}")))?;
if was_unread {
db::folders::adjust_unread_count(&conn, &body.to_folder, 1)
db::folders::adjust_unread_count(&transaction, &body.to_folder, 1)
.map_err(|e| AppError::InternalError(format!("Database error: {e}")))?;
}
db::folders::invalidate_folder_freshness(&conn, &body.to_folder)
db::folders::invalidate_folder_freshness(&transaction, &body.to_folder)
.map_err(|e| AppError::InternalError(format!("Database error: {e}")))?;
transaction
.commit()
.map_err(|e| AppError::InternalError(format!("Database commit error: {e}")))?;
let undo_move = undo_destination_uid.map(|destination_uid| {
serde_json::json!({
@@ -1454,6 +1470,7 @@ mod sync_tests {
limit: 10,
..Default::default()
})
.await
.unwrap()
.0
.is_empty()
@@ -1646,12 +1663,14 @@ mod sync_tests {
.is_none()
);
assert!(db::folders::is_folder_fresh(&conn, "INBOX", 30).unwrap());
let requests = fresh_client.header_requests.lock().unwrap();
assert_eq!(requests.len(), 4);
assert!(requests.iter().all(|(_, uids)| {
uids.split(',')
.all(|uid| uid.parse::<u32>().unwrap() <= 900)
}));
{
let requests = fresh_client.header_requests.lock().unwrap();
assert_eq!(requests.len(), 4);
assert!(requests.iter().all(|(_, uids)| {
uids.split(',')
.all(|uid| uid.parse::<u32>().unwrap() <= 900)
}));
}
let results = engine
.open_user_index(user_hash)
.unwrap()
@@ -1661,6 +1680,7 @@ mod sync_tests {
limit: 1100,
..Default::default()
})
.await
.unwrap()
.0;
assert!(results.iter().any(|message| message.uid == 1));