//
// Copyright (c) 2025 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::modules::account::migration::{AccountV1, AccountV2, AccountV3};
use crate::modules::autoconfig::CachedMailSettings;
use crate::modules::error::code::ErrorCode;
use crate::modules::error::BichonResult;
use crate::modules::oauth2::entity::OAuth2;
use crate::modules::oauth2::pending::OAuth2PendingEntity;
use crate::modules::oauth2::token::OAuth2AccessToken;
use crate::modules::settings::proxy::Proxy;
use crate::modules::settings::system::SystemSetting;
use crate::modules::token::AccessTokenModel;
use crate::modules::users::role::UserRole;
use crate::modules::users::{BichonUser, BichonUserV2};
use crate::raise_error;
use db_type::{KeyOptions, ToKeyDefinition};
use itertools::Itertools;
use native_db::*;
use serde::Serialize;
use std::sync::{Arc, LazyLock};
use transaction::RwTransaction;
pub mod manager;
pub static META_MODELS: LazyLock = LazyLock::new(|| {
let mut adapter = ModelsAdapter::new();
adapter.register_metadata_models();
adapter.models
});
pub struct ModelsAdapter {
pub models: Models,
}
impl ModelsAdapter {
pub fn new() -> Self {
ModelsAdapter {
models: Models::new(),
}
}
pub fn register_model(&mut self) {
self.models.define::().expect("failed to define model ");
}
pub fn register_metadata_models(&mut self) {
//Starting from version 0.2.0, `AccessToken` is deprecated/no longer used, but its ID must not be reused, otherwise it may cause model errors.
//self.register_model::();
self.register_model::();
self.register_model::();
self.register_model::();
self.register_model::();
self.register_model::();
self.register_model::();
self.register_model::();
self.register_model::();
self.register_model::();
self.register_model::();
self.register_model::();
self.register_model::();
self.register_model::();
}
}
pub async fn insert_impl(
database: &Arc>,
item: T,
) -> BichonResult<()> {
let db = database.clone();
tokio::task::spawn_blocking(move || {
let rw_transaction = db
.rw_transaction()
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
rw_transaction
.insert(item)
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
rw_transaction
.commit()
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
Ok(())
})
.await
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?
}
pub async fn batch_insert_impl(
database: &Arc>,
batch: Vec,
) -> BichonResult<()> {
let db = database.clone();
tokio::task::spawn_blocking(move || {
let rw_transaction = db
.rw_transaction()
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
for item in batch {
rw_transaction
.insert(item)
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
}
rw_transaction
.commit()
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
Ok(())
})
.await
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?
}
pub async fn batch_upsert_impl(
database: &Arc>,
batch: Vec,
) -> BichonResult<()> {
let db = database.clone();
tokio::task::spawn_blocking(move || {
let rw_transaction = db
.rw_transaction()
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
for item in batch {
rw_transaction
.upsert(item)
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
}
rw_transaction
.commit()
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
Ok(())
})
.await
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?
}
pub async fn upsert_impl(
database: &Arc>,
item: T,
) -> BichonResult<()> {
let db = database.clone();
tokio::task::spawn_blocking(move || {
let rw_transaction = db
.rw_transaction()
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
rw_transaction
.upsert(item)
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
rw_transaction
.commit()
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
Ok(())
})
.await
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?
}
pub async fn update_impl(
database: &Arc>,
current: impl FnOnce(&RwTransaction) -> BichonResult + Send + 'static,
updated: impl FnOnce(&T) -> BichonResult + Send + 'static,
) -> BichonResult {
let db = database.clone();
tokio::task::spawn_blocking(move || {
let rw = db
.rw_transaction()
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
let current_item = current(&rw)?;
let updated_item = updated(¤t_item)?;
rw.update(current_item, updated_item.clone())
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
rw.commit()
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
Ok(updated_item)
})
.await
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?
}
// pub async fn batch_update_impl(
// database: &Arc>,
// filter: impl FnOnce(&RwTransaction) -> RustMailerResult> + Send + 'static,
// updated: impl FnOnce(&Vec) -> RustMailerResult> + Send + 'static,
// ) -> RustMailerResult> {
// let db = database.clone();
// tokio::task::spawn_blocking(move || {
// let rw = db
// .rw_transaction()
// .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
// let targets = filter(&rw)?;
// let tuples = updated(&targets)?;
// for (old, updated) in tuples {
// rw.update(old, updated)
// .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
// }
// rw.commit()
// .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
// Ok(targets)
// })
// .await
// .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?
// }
pub async fn async_find_impl(
database: &Arc>,
key: impl ToKey + Send + 'static,
) -> BichonResult