mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-03 07:48:34 +02:00
fix: make email/login_name immutable and add ui sortable account_name #195
This commit is contained in:
@@ -29,8 +29,8 @@ use crate::{
|
||||
modules::{
|
||||
account::{
|
||||
entity::ImapConfig,
|
||||
state::DownloadState,
|
||||
since::{DateSince, RelativeDate},
|
||||
state::DownloadState,
|
||||
},
|
||||
cache::imap::mailbox::MailBox,
|
||||
database::{list_all_impl, secondary_find_impl, with_transaction},
|
||||
@@ -67,6 +67,15 @@ pub enum AccountType {
|
||||
NoSync,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize, Enum)]
|
||||
pub enum QuotaWindow {
|
||||
Hourly,
|
||||
#[default]
|
||||
Daily,
|
||||
Weekly,
|
||||
Monthly,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize, Object)]
|
||||
#[native_model(id = 4, version = 1)]
|
||||
#[native_db(primary_key(pk -> String))]
|
||||
@@ -170,15 +179,16 @@ pub struct AccountV4 {
|
||||
pub enabled: bool,
|
||||
#[oai(validator(custom = "crate::modules::common::validator::EmailValidator"))]
|
||||
pub email: String,
|
||||
pub name: Option<String>,
|
||||
pub account_name: Option<String>,
|
||||
pub login_name: Option<String>,
|
||||
pub capabilities: Option<Vec<String>>,
|
||||
pub date_since: Option<DateSince>,
|
||||
pub date_before: Option<RelativeDate>,
|
||||
pub folder_limit: Option<u32>,
|
||||
pub sync_folders: Option<Vec<String>>,
|
||||
pub download_folders: Option<Vec<String>>,
|
||||
pub account_type: AccountType,
|
||||
pub sync_interval_min: Option<i64>,
|
||||
pub sync_batch_size: Option<u32>,
|
||||
pub download_interval_min: Option<i64>,
|
||||
pub download_batch_size: Option<u32>,
|
||||
pub known_folders: Option<BTreeSet<String>>,
|
||||
pub created_at: i64,
|
||||
pub updated_at: i64,
|
||||
@@ -186,8 +196,9 @@ pub struct AccountV4 {
|
||||
pub use_proxy: Option<u64>,
|
||||
pub use_dangerous: bool,
|
||||
pub pgp_key: Option<String>,
|
||||
pub imap_daily_quota_bytes: Option<u32>,
|
||||
pub auto_sync_new_mailboxes: Option<bool>,
|
||||
pub imap_quota_bytes: Option<u64>,
|
||||
pub imap_quota_window: Option<QuotaWindow>,
|
||||
pub auto_download_new_mailboxes: Option<bool>,
|
||||
}
|
||||
|
||||
impl AccountV4 {
|
||||
@@ -199,15 +210,16 @@ impl AccountV4 {
|
||||
Ok(Self {
|
||||
id: id!(64),
|
||||
email: request.email,
|
||||
name: request.name,
|
||||
login_name: request.login_name,
|
||||
account_name: request.account_name,
|
||||
imap: request.imap.map(|i| i.try_encrypt_password()).transpose()?,
|
||||
enabled: request.enabled,
|
||||
capabilities: None,
|
||||
date_since: request.date_since,
|
||||
sync_folders: None,
|
||||
download_folders: None,
|
||||
known_folders: None,
|
||||
account_type: request.account_type,
|
||||
sync_interval_min: request.sync_interval_min,
|
||||
download_interval_min: request.download_interval_min,
|
||||
created_at: utc_now!(),
|
||||
updated_at: utc_now!(),
|
||||
use_proxy: request.use_proxy,
|
||||
@@ -215,10 +227,11 @@ impl AccountV4 {
|
||||
use_dangerous: request.use_dangerous,
|
||||
pgp_key: request.pgp_key,
|
||||
created_by: user_id,
|
||||
sync_batch_size: request.sync_batch_size,
|
||||
download_batch_size: request.download_batch_size,
|
||||
date_before: request.date_before,
|
||||
imap_daily_quota_bytes: request.imap_daily_quota_bytes,
|
||||
auto_sync_new_mailboxes: request.auto_sync_new_mailboxes,
|
||||
auto_download_new_mailboxes: request.auto_download_new_mailboxes,
|
||||
imap_quota_bytes: request.imap_quota_bytes,
|
||||
imap_quota_window: request.imap_quota_window,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -374,7 +387,7 @@ impl AccountV4 {
|
||||
.ok_or_else(|| raise_error!(format!("When trying to update account download folders, the corresponding record was not found. account_id={}", account_id), ErrorCode::ResourceNotFound))
|
||||
}, |current|{
|
||||
let mut updated = current.clone();
|
||||
updated.sync_folders = Some(download_folders);
|
||||
updated.download_folders = Some(download_folders);
|
||||
Ok(updated)
|
||||
}).await?;
|
||||
Ok(())
|
||||
@@ -490,14 +503,6 @@ impl AccountV4 {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(name) = &request.name {
|
||||
if name.trim().is_empty() {
|
||||
new.name = None;
|
||||
} else {
|
||||
new.name = Some(name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(old.account_type, AccountType::IMAP) {
|
||||
if let Some(imap) = &request.imap {
|
||||
if let Some(current_imap) = &mut new.imap {
|
||||
@@ -514,14 +519,14 @@ impl AccountV4 {
|
||||
}
|
||||
|
||||
if let Some(folder_names) = request.sync_folders {
|
||||
new.sync_folders = Some(folder_names);
|
||||
new.download_folders = Some(folder_names);
|
||||
}
|
||||
if let Some(sync_interval_min) = &request.sync_interval_min {
|
||||
new.sync_interval_min = Some(*sync_interval_min);
|
||||
if let Some(sync_interval_min) = &request.download_interval_min {
|
||||
new.download_interval_min = Some(*sync_interval_min);
|
||||
}
|
||||
|
||||
if let Some(sync_batch_size) = &request.sync_batch_size {
|
||||
new.sync_batch_size = Some(*sync_batch_size);
|
||||
if let Some(download_batch_size) = &request.download_batch_size {
|
||||
new.download_batch_size = Some(*download_batch_size);
|
||||
}
|
||||
|
||||
if let Some(use_proxy) = request.use_proxy {
|
||||
@@ -547,12 +552,16 @@ impl AccountV4 {
|
||||
new.pgp_key = Some(pgp_key);
|
||||
}
|
||||
|
||||
if let Some(imap_daily_quota_bytes) = request.imap_daily_quota_bytes {
|
||||
new.imap_daily_quota_bytes = Some(imap_daily_quota_bytes);
|
||||
if let Some(imap_quota_bytes) = request.imap_quota_bytes {
|
||||
new.imap_quota_bytes = Some(imap_quota_bytes);
|
||||
}
|
||||
|
||||
if let Some(auto_sync_new_mailboxes) = request.auto_sync_new_mailboxes {
|
||||
new.auto_sync_new_mailboxes = Some(auto_sync_new_mailboxes);
|
||||
if let Some(imap_quota_window) = request.imap_quota_window {
|
||||
new.imap_quota_window = Some(imap_quota_window);
|
||||
}
|
||||
|
||||
if let Some(auto_download_new_mailboxes) = request.auto_download_new_mailboxes {
|
||||
new.auto_download_new_mailboxes = Some(auto_download_new_mailboxes);
|
||||
}
|
||||
new.updated_at = utc_now!();
|
||||
Ok(new)
|
||||
@@ -663,15 +672,15 @@ impl From<AccountV4> for AccountV3 {
|
||||
imap: value.imap,
|
||||
enabled: value.enabled,
|
||||
email: value.email,
|
||||
name: value.name,
|
||||
name: value.login_name,
|
||||
capabilities: value.capabilities,
|
||||
date_since: value.date_since,
|
||||
date_before: value.date_before,
|
||||
folder_limit: value.folder_limit,
|
||||
sync_folders: value.sync_folders,
|
||||
sync_folders: value.download_folders,
|
||||
account_type: value.account_type,
|
||||
sync_interval_min: value.sync_interval_min,
|
||||
sync_batch_size: value.sync_batch_size,
|
||||
sync_interval_min: value.download_interval_min,
|
||||
sync_batch_size: value.download_batch_size,
|
||||
known_folders: value.known_folders,
|
||||
created_at: value.created_at,
|
||||
updated_at: value.updated_at,
|
||||
@@ -690,15 +699,16 @@ impl From<AccountV3> for AccountV4 {
|
||||
imap: value.imap,
|
||||
enabled: value.enabled,
|
||||
email: value.email,
|
||||
name: value.name,
|
||||
account_name: None,
|
||||
login_name: value.name,
|
||||
capabilities: value.capabilities,
|
||||
date_since: value.date_since,
|
||||
date_before: value.date_before,
|
||||
folder_limit: value.folder_limit,
|
||||
sync_folders: value.sync_folders,
|
||||
download_folders: value.sync_folders,
|
||||
account_type: value.account_type,
|
||||
sync_interval_min: value.sync_interval_min,
|
||||
sync_batch_size: value.sync_batch_size,
|
||||
download_interval_min: value.sync_interval_min,
|
||||
download_batch_size: value.sync_batch_size,
|
||||
known_folders: value.known_folders,
|
||||
created_at: value.created_at,
|
||||
updated_at: value.updated_at,
|
||||
@@ -706,8 +716,9 @@ impl From<AccountV3> for AccountV4 {
|
||||
use_proxy: value.use_proxy,
|
||||
use_dangerous: value.use_dangerous,
|
||||
pgp_key: value.pgp_key,
|
||||
imap_daily_quota_bytes: None,
|
||||
auto_sync_new_mailboxes: None,
|
||||
imap_quota_window: None,
|
||||
imap_quota_bytes: None,
|
||||
auto_download_new_mailboxes: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::modules::account::entity::ImapConfig;
|
||||
use crate::modules::account::migration::{AccountModel, AccountType};
|
||||
use crate::modules::account::migration::{AccountModel, AccountType, QuotaWindow};
|
||||
use crate::modules::account::since::{DateSince, RelativeDate};
|
||||
use crate::modules::error::code::ErrorCode;
|
||||
use crate::modules::error::BichonResult;
|
||||
@@ -29,7 +29,8 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct AccountCreateRequest {
|
||||
#[oai(validator(custom = "crate::modules::common::validator::EmailValidator"))]
|
||||
pub email: String,
|
||||
pub name: Option<String>,
|
||||
pub login_name: Option<String>,
|
||||
pub account_name: Option<String>,
|
||||
pub imap: Option<ImapConfig>,
|
||||
pub enabled: bool,
|
||||
pub date_since: Option<DateSince>,
|
||||
@@ -38,14 +39,15 @@ pub struct AccountCreateRequest {
|
||||
#[oai(validator(minimum(value = "100")))]
|
||||
pub folder_limit: Option<u32>,
|
||||
#[oai(validator(minimum(value = "10")))]
|
||||
pub sync_interval_min: Option<i64>,
|
||||
pub download_interval_min: Option<i64>,
|
||||
#[oai(validator(minimum(value = "10"), maximum(value = "200")))]
|
||||
pub sync_batch_size: Option<u32>,
|
||||
pub download_batch_size: Option<u32>,
|
||||
pub use_proxy: Option<u64>,
|
||||
pub use_dangerous: bool,
|
||||
pub pgp_key: Option<String>,
|
||||
pub imap_daily_quota_bytes: Option<u32>,
|
||||
pub auto_sync_new_mailboxes: Option<bool>,
|
||||
pub imap_quota_bytes: Option<u64>,
|
||||
pub imap_quota_window: Option<QuotaWindow>,
|
||||
pub auto_download_new_mailboxes: Option<bool>,
|
||||
}
|
||||
|
||||
impl AccountCreateRequest {
|
||||
@@ -58,6 +60,13 @@ impl AccountCreateRequest {
|
||||
));
|
||||
}
|
||||
|
||||
if self.imap_quota_bytes.is_some() ^ self.imap_quota_window.is_some() {
|
||||
return Err(raise_error!(
|
||||
"Quota bytes and quota window must be provided together or omitted together".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(date_since) = self.date_since.as_ref() {
|
||||
date_since.validate()?;
|
||||
}
|
||||
@@ -77,7 +86,7 @@ impl AccountCreateRequest {
|
||||
))
|
||||
}
|
||||
}
|
||||
if self.sync_interval_min.is_none() {
|
||||
if self.download_interval_min.is_none() {
|
||||
return Err(raise_error!(
|
||||
"`sync_interval_min` is required for IMAP account type".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
@@ -107,8 +116,7 @@ pub struct AccountUpdateRequest {
|
||||
/// and any attempts to access them should return an error indicating the account
|
||||
/// is inactive.
|
||||
pub enabled: Option<bool>,
|
||||
/// Display name for the account (optional)
|
||||
pub name: Option<String>,
|
||||
pub account_name: Option<String>,
|
||||
/// IMAP server configuration
|
||||
pub imap: Option<ImapConfig>,
|
||||
/// Controls initial synchronization time range
|
||||
@@ -146,9 +154,9 @@ pub struct AccountUpdateRequest {
|
||||
pub sync_folders: Option<Vec<String>>,
|
||||
/// Incremental sync interval (seconds)
|
||||
#[oai(validator(minimum(value = "10")))]
|
||||
pub sync_interval_min: Option<i64>,
|
||||
pub download_interval_min: Option<i64>,
|
||||
#[oai(validator(minimum(value = "10"), maximum(value = "200")))]
|
||||
pub sync_batch_size: Option<u32>,
|
||||
pub download_batch_size: Option<u32>,
|
||||
/// Optional proxy ID for establishing the connection to external APIs (e.g., Gmail, Outlook).
|
||||
/// - If `None` or not provided, the client will connect directly to the API server.
|
||||
/// - If `Some(proxy_id)`, the client will use the pre-configured proxy with the given ID for API requests.
|
||||
@@ -157,8 +165,9 @@ pub struct AccountUpdateRequest {
|
||||
pub use_dangerous: Option<bool>,
|
||||
|
||||
pub pgp_key: Option<String>,
|
||||
pub imap_daily_quota_bytes: Option<u32>,
|
||||
pub auto_sync_new_mailboxes: Option<bool>,
|
||||
pub imap_quota_bytes: Option<u64>,
|
||||
pub imap_quota_window: Option<QuotaWindow>,
|
||||
pub auto_download_new_mailboxes: Option<bool>,
|
||||
}
|
||||
|
||||
impl AccountUpdateRequest {
|
||||
@@ -171,6 +180,13 @@ impl AccountUpdateRequest {
|
||||
));
|
||||
}
|
||||
|
||||
if self.imap_quota_bytes.is_some() ^ self.imap_quota_window.is_some() {
|
||||
return Err(raise_error!(
|
||||
"Quota bytes and quota window must be provided together or omitted together".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
));
|
||||
}
|
||||
|
||||
if self.clear_folder_limit == Some(true) && self.folder_limit.is_some() {
|
||||
return Err(raise_error!(
|
||||
"clear_folder_limit cannot be combined with folder_limit".into(),
|
||||
|
||||
+17
-11
@@ -24,7 +24,7 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::modules::{
|
||||
account::{
|
||||
entity::ImapConfig,
|
||||
migration::{AccountModel, AccountType},
|
||||
migration::{AccountModel, AccountType, QuotaWindow},
|
||||
since::{DateSince, RelativeDate},
|
||||
},
|
||||
users::UserModel,
|
||||
@@ -36,15 +36,16 @@ pub struct AccountResp {
|
||||
pub imap: Option<ImapConfig>,
|
||||
pub enabled: bool,
|
||||
pub email: String,
|
||||
pub name: Option<String>,
|
||||
pub account_name: Option<String>,
|
||||
pub login_name: Option<String>,
|
||||
pub capabilities: Option<Vec<String>>,
|
||||
pub date_since: Option<DateSince>,
|
||||
pub date_before: Option<RelativeDate>,
|
||||
pub folder_limit: Option<u32>,
|
||||
pub sync_folders: Option<Vec<String>>,
|
||||
pub download_folders: Option<Vec<String>>,
|
||||
pub account_type: AccountType,
|
||||
pub sync_interval_min: Option<i64>,
|
||||
pub sync_batch_size: Option<u32>,
|
||||
pub download_interval_min: Option<i64>,
|
||||
pub download_batch_size: Option<u32>,
|
||||
pub known_folders: Option<BTreeSet<String>>,
|
||||
pub created_at: i64,
|
||||
pub updated_at: i64,
|
||||
@@ -54,7 +55,9 @@ pub struct AccountResp {
|
||||
pub use_proxy: Option<u64>,
|
||||
pub use_dangerous: bool,
|
||||
pub pgp_key: Option<String>,
|
||||
pub imap_daily_quota_bytes: Option<u32>,
|
||||
pub imap_quota_bytes: Option<u64>,
|
||||
pub imap_quota_window: Option<QuotaWindow>,
|
||||
pub auto_download_new_mailboxes: Option<bool>,
|
||||
}
|
||||
|
||||
impl AccountResp {
|
||||
@@ -65,15 +68,16 @@ impl AccountResp {
|
||||
imap: account.imap,
|
||||
enabled: account.enabled,
|
||||
email: account.email,
|
||||
name: account.name,
|
||||
account_name: account.account_name,
|
||||
login_name: account.login_name,
|
||||
capabilities: account.capabilities,
|
||||
date_since: account.date_since,
|
||||
date_before: account.date_before,
|
||||
folder_limit: account.folder_limit,
|
||||
sync_folders: account.sync_folders,
|
||||
download_folders: account.download_folders,
|
||||
account_type: account.account_type,
|
||||
sync_interval_min: account.sync_interval_min,
|
||||
sync_batch_size: account.sync_batch_size,
|
||||
download_interval_min: account.download_interval_min,
|
||||
download_batch_size: account.download_batch_size,
|
||||
known_folders: account.known_folders,
|
||||
created_at: account.created_at,
|
||||
updated_at: account.updated_at,
|
||||
@@ -87,7 +91,9 @@ impl AccountResp {
|
||||
use_proxy: account.use_proxy,
|
||||
use_dangerous: account.use_dangerous,
|
||||
pgp_key: account.pgp_key,
|
||||
imap_daily_quota_bytes: account.imap_daily_quota_bytes,
|
||||
imap_quota_bytes: account.imap_quota_bytes,
|
||||
imap_quota_window: account.imap_quota_window,
|
||||
auto_download_new_mailboxes: account.auto_download_new_mailboxes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user