mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-07-18 04:24:07 +00:00
183 lines
6.2 KiB
Rust
183 lines
6.2 KiB
Rust
use crate::user_discovery::UserDiscovery;
|
|
use crate::{
|
|
bridge::{
|
|
callbacks::user_discovery::{UserDiscoveryStoreFlutter, UserDiscoveryUtilsFlutter},
|
|
InitConfig,
|
|
},
|
|
database::Database,
|
|
error::{Result, TwonlyError},
|
|
keys::{DatabaseKey, KeyManager},
|
|
log::init_tracing,
|
|
utils::Shared,
|
|
};
|
|
use std::{path::PathBuf, sync::Arc};
|
|
use tokio::sync::{Mutex, OnceCell};
|
|
use zeroize::Zeroize;
|
|
|
|
use crate::{bridge::TwonlyFlutter, secure_storage::SecureStorage, standalone::TwonlyStandalone};
|
|
|
|
pub(crate) enum Context {
|
|
Flutter(TwonlyFlutter),
|
|
Standalone(TwonlyStandalone),
|
|
}
|
|
|
|
impl Context {
|
|
#[cfg(test)]
|
|
pub(crate) fn from_standalone(standalone: TwonlyStandalone) -> Self {
|
|
Self::Standalone(standalone)
|
|
}
|
|
}
|
|
|
|
static GLOBAL_CONTEXT: OnceCell<Context> = OnceCell::const_new();
|
|
|
|
impl Context {
|
|
pub(crate) async fn init_flutter(config: InitConfig) -> Result<()> {
|
|
Self::init_common(config, true).await
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub(crate) async fn init_standalone(config: InitConfig) -> Result<()> {
|
|
Self::init_common(config, false).await
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub(crate) async fn init_for_testing(
|
|
database_dir: PathBuf,
|
|
data_dir: PathBuf,
|
|
) -> Result<Context> {
|
|
use tokio::sync::Mutex;
|
|
|
|
std::fs::create_dir_all(&database_dir)?;
|
|
std::fs::create_dir_all(&data_dir)?;
|
|
|
|
let config = InitConfig {
|
|
database_dir: database_dir.display().to_string(),
|
|
data_dir: data_dir.display().to_string(),
|
|
};
|
|
|
|
// Initialize tracing and secure storage if not already done
|
|
let _ = SecureStorage::init();
|
|
let secure_storage = SecureStorage::new("eu.twonly.testing");
|
|
|
|
let key_manager = KeyManager::generate()?;
|
|
key_manager.store_to_keychain(&secure_storage)?;
|
|
|
|
let rust_db_path = database_dir.join("rust_db.sqlite");
|
|
let rust_db = Database::new(
|
|
&rust_db_path.display().to_string(),
|
|
Some(&key_manager.main_key.get_database_key(DatabaseKey::RustDb)),
|
|
false,
|
|
)
|
|
.await?;
|
|
rust_db.run_migrations().await?;
|
|
let rust_db = Arc::new(rust_db);
|
|
|
|
Ok(Context::from_standalone(TwonlyStandalone {
|
|
config,
|
|
rust_db,
|
|
secure_storage,
|
|
key_manager: Arc::new(Mutex::new(key_manager)),
|
|
}))
|
|
}
|
|
|
|
async fn init_common(config: InitConfig, is_flutter: bool) -> Result<()> {
|
|
if GLOBAL_CONTEXT.initialized() {
|
|
tracing::info!("twonly already initialized. Ensuring storage directories exist.");
|
|
std::fs::create_dir_all(&config.database_dir)?;
|
|
std::fs::create_dir_all(&config.data_dir)?;
|
|
return Ok(());
|
|
}
|
|
|
|
std::fs::create_dir_all(&config.database_dir)?;
|
|
std::fs::create_dir_all(&config.data_dir)?;
|
|
|
|
let log_dir = PathBuf::from(&config.data_dir).join("log");
|
|
init_tracing(&log_dir, is_flutter).await;
|
|
|
|
SecureStorage::init()?;
|
|
let secure_storage = SecureStorage::new("eu.twonly");
|
|
|
|
let database_dir = PathBuf::from(&config.database_dir.clone());
|
|
let rust_db_path = database_dir.join("rust_db.sqlite");
|
|
|
|
tracing::info!("Initialized twonly workspace.");
|
|
let res: Result<&'static Context> = GLOBAL_CONTEXT
|
|
.get_or_try_init(|| async {
|
|
let key_manager = match KeyManager::try_from_keychain(&secure_storage) {
|
|
Ok(key) => key,
|
|
Err(err) => {
|
|
tracing::error!("{err}");
|
|
if rust_db_path.exists() {
|
|
tracing::error!("Rust Database exists, while the key manager not. This must be a secure storage error.");
|
|
return Err(TwonlyError::SecureStorageError);
|
|
}
|
|
tracing::info!("Generating a new key manager.");
|
|
let new = KeyManager::generate()?;
|
|
new.store_to_keychain(&secure_storage)?;
|
|
new
|
|
}
|
|
};
|
|
|
|
let mut rust_db_key = key_manager.main_key.get_database_key(DatabaseKey::RustDb);
|
|
|
|
let rust_db = Database::new(
|
|
&rust_db_path.display().to_string(),
|
|
Some(rust_db_key.as_str()),
|
|
false,
|
|
)
|
|
.await?;
|
|
rust_db.run_migrations().await?;
|
|
let rust_db = Arc::new(rust_db);
|
|
|
|
rust_db_key.zeroize();
|
|
|
|
if is_flutter {
|
|
Ok(Context::Flutter(TwonlyFlutter {
|
|
config,
|
|
secure_storage,
|
|
rust_db,
|
|
key_manager: Arc::new(Mutex::new(key_manager)),
|
|
user_discovery: Shared::new(UserDiscovery::new(
|
|
UserDiscoveryStoreFlutter {},
|
|
UserDiscoveryUtilsFlutter {},
|
|
)?),
|
|
}))
|
|
} else {
|
|
Ok(Context::Standalone(TwonlyStandalone {
|
|
config,
|
|
rust_db,
|
|
key_manager: Arc::new(Mutex::new(key_manager)),
|
|
secure_storage,
|
|
}))
|
|
}
|
|
})
|
|
.await;
|
|
res?;
|
|
Ok(())
|
|
}
|
|
|
|
pub(super) fn get_static() -> Result<&'static Context> {
|
|
GLOBAL_CONTEXT.get().ok_or(TwonlyError::Initialization)
|
|
}
|
|
|
|
// pub(crate) fn get_secure_storage(&self) -> Result<&SecureStorage> {
|
|
// match self {
|
|
// Self::Flutter(twonly) => Ok(&twonly.secure_storage),
|
|
// Self::Standalone(twonly) => Ok(&twonly.secure_storage),
|
|
// }
|
|
// }
|
|
|
|
pub(crate) fn get_config(&self) -> Result<&InitConfig> {
|
|
match self {
|
|
Self::Flutter(twonly) => Ok(&twonly.config),
|
|
Self::Standalone(twonly) => Ok(&twonly.config),
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn get_key_manager(&self) -> Result<tokio::sync::MutexGuard<'_, KeyManager>> {
|
|
match self {
|
|
Self::Flutter(twonly) => Ok(twonly.key_manager.lock().await),
|
|
Self::Standalone(twonly) => Ok(twonly.key_manager.lock().await),
|
|
}
|
|
}
|
|
}
|