//
// Copyright (c) 2025-2026 rustmailer.com (https://rustmailer.com)
//
// This file is part of the Bichon Email Archiving Project
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
use crate::common::auth::WrappedContext;
use crate::rest::api::ApiTags;
use crate::rest::ApiResult;
use bichon_core::account::migration::AccountModel;
use bichon_core::common::paginated::DataPage;
use bichon_core::error::code::ErrorCode;
use bichon_core::message::append::restore_emails;
use bichon_core::message::append::RestoreMessagesRequest;
use bichon_core::message::attachment::retrieve_attachment_content;
use bichon_core::message::attachment::retrieve_nested_attachment_content;
use bichon_core::message::content::retrieve_nested_eml_content;
use bichon_core::message::content::FullNestedMessageContent;
use bichon_core::message::content::{retrieve_email_content, FullMessageContent};
use bichon_core::message::delete::delete_messages_impl;
use bichon_core::message::list::get_thread_messages;
use bichon_core::message::search::{search_messages_impl, EmailSearchRequest};
use bichon_core::message::tags::TagCount;
use bichon_core::message::tags::TagsRequest;
use bichon_core::raise_error;
use bichon_core::store::blob::get_reader;
use bichon_core::store::envelope::Envelope;
use bichon_core::store::tantivy::envelope::ENVELOPE_MANAGER;
use bichon_core::store::tantivy::validate_facet;
use bichon_core::users::permissions::Permission;
use poem::Body;
use poem_openapi::param::{Path, Query};
use poem_openapi::payload::{Attachment, AttachmentType, Json};
use poem_openapi::OpenApi;
use std::collections::HashMap;
use std::collections::HashSet;
pub struct MessageApi;
#[OpenApi(prefix_path = "/api/v1", tag = "ApiTags::Message")]
impl MessageApi {
/// Deletes messages from a mailbox or moves them to the trash for the specified account.
#[oai(
path = "/delete-messages",
method = "post",
operation_id = "delete_messages"
)]
async fn delete_messages(
&self,
/// specifying the mailbox and messages to delete.
payload: Json>>,
context: WrappedContext,
) -> ApiResult<()> {
let request = payload.0;
for account_id in request.keys() {
context.require_permission(Some(*account_id), Permission::DATA_DELETE)?;
}
Ok(delete_messages_impl(request).await?)
}
/// Searches messages across all mailboxes using various filter criteria.
/// The search filters are provided in the request body.
#[oai(
path = "/search-messages",
method = "post",
operation_id = "search_messages"
)]
async fn search_messages(
&self,
payload: Json,
context: WrappedContext,
) -> ApiResult>> {
let authorized_ids: Option> =
if context.has_permission(None, Permission::DATA_READ_ALL) {
None
} else {
Some(context.user.account_access_map.keys().cloned().collect())
};
Ok(Json(search_messages_impl(authorized_ids, payload.0)?))
}
/// Retrieves all messages belonging to a specific thread. Requires `thread_id`, `page`, and `page_size` query parameters.
#[oai(
path = "/get-thread-messages/:account_id",
method = "get",
operation_id = "get_thread_messages"
)]
async fn get_thread_messages(
&self,
/// The ID of the account owning the mailbox.
account_id: Path,
// Thread ID
thread_id: Query,
/// The page number for pagination (1-based).
page: Query,
/// The number of messages per page.
page_size: Query,
context: WrappedContext,
) -> ApiResult>> {
let account_id = account_id.0;
let thread_id = thread_id.0.trim();
context.require_permission(Some(account_id), Permission::DATA_READ)?;
Ok(Json(get_thread_messages(
account_id,
thread_id,
page.0,
page_size.0,
)?))
}
/// Fetches the content of a specific email.
/// Set `block_remote_content=true` to strip external images, scripts,
/// and other content loaded from http/https URLs.
#[oai(
path = "/message-content/:account_id/:envelope_id",
method = "get",
operation_id = "fetch_message_content"
)]
async fn fetch_message_content(
&self,
/// The ID of the account.
account_id: Path,
/// The ID of the message to fetch.
envelope_id: Path,
/// Block remote content (http/https URLs) from email body.
block_remote_content: Query