use std::{collections::HashMap, fs, path::Path, sync::Arc};
use migration::MigratorTrait;
use sea_orm::{Database, DatabaseConnection};
use signer_core::{SignerFsStore, SignerUser};
use tokio::sync::RwLock;
use crate::{
ChatStore, CrdtAdapterController, CrdtEventController, DeltaBoxNotifier, MessageStore,
RemoteController, ServerStore, UserStore, signer_remote::SignerRemote,
};
pub struct SignerDaemon {
pub core: SignerDaemonCore,
pub state: SignerDaemonState,
pub option: SignerDaemonOption,
pub controller: SignerDaemonController,
pub store: SignerDaemonStore,
}
#[derive(Debug, Clone)]
pub struct SignerDaemonCore {
pub db: DatabaseConnection,
pub peer: String,
pub prv_key: String,
pub pub_key: String,
pub change_notifier: DeltaBoxNotifier,
}
pub struct SignerDaemonState {
pub user: SignerUser,
pub remotes: HashMap<String, SignerRemote>,
pub daemon: Option<Arc<RwLock<SignerDaemon>>>,
}
#[derive(Debug, Clone)]
pub struct SignerDaemonOption {
pub(crate) user_file_path: Option<String>,
}
pub struct SignerDaemonController {
pub crdt_event: CrdtEventController,
pub crdt_adapter: CrdtAdapterController,
pub remote: RemoteController,
}
pub struct SignerDaemonStore {
pub chat: ChatStore,
pub message: MessageStore,
pub user: UserStore,
pub server: ServerStore,
}
impl SignerDaemon {
pub async fn from_memory(
user: &SignerUser,
peer: &str,
) -> crate::DaemonResult<Arc<RwLock<Self>>> {
let db = Database::connect("sqlite::memory:").await?;
migration::Migrator::up(&db, None).await.map_err(|e| {
crate::DaemonError::Signer(crate::SignerError::Msg(format!(
"running migration failed: {}",
e
)))
})?;
let core = SignerDaemonCore {
db,
peer: peer.to_string(),
prv_key: user.prv_key.clone(),
pub_key: user.public.pub_key.clone(),
change_notifier: DeltaBoxNotifier::new(),
};
let option = SignerDaemonOption {
user_file_path: None,
};
let daemon = Self {
core: core.clone(),
state: SignerDaemonState {
user: user.clone(),
remotes: HashMap::new(),
daemon: None,
},
option: option.clone(),
controller: SignerDaemonController {
crdt_event: CrdtEventController::new(core.clone()),
crdt_adapter: CrdtAdapterController::new(core.clone()),
remote: RemoteController::new(core.clone()),
},
store: SignerDaemonStore {
chat: ChatStore::new(core.clone()),
message: MessageStore::new(core.clone()),
user: UserStore::new(core.clone(), option.clone()),
server: ServerStore::new(core.clone()),
},
};
daemon.start().await
}
pub async fn from_path(
user: &SignerUser,
path: &str,
user_file_name: &str,
) -> crate::DaemonResult<Arc<RwLock<Self>>> {
let db_url = format!("sqlite://{}/signer.sqlite3?mode=rwc", path);
let db = Database::connect(&db_url).await?;
migration::Migrator::up(&db, None).await.map_err(|e| {
crate::DaemonError::Signer(crate::SignerError::Msg(format!(
"running migration failed: {}",
e
)))
})?;
let peer = match fs::read_to_string(Path::new(path).join("peer")) {
Ok(val) => val,
Err(_) => {
let new_peer = uuid::Uuid::new_v4().to_string();
fs::write(Path::new(path).join("peer"), &new_peer)?;
new_peer
}
};
let core = SignerDaemonCore {
db,
peer: peer.clone(),
prv_key: user.prv_key.clone(),
pub_key: user.public.pub_key.clone(),
change_notifier: DeltaBoxNotifier::new(),
};
let option = SignerDaemonOption {
user_file_path: Some(
Path::new(path)
.join(user_file_name)
.to_str()
.unwrap()
.to_string(),
),
};
let daemon = Self {
core: core.clone(),
state: SignerDaemonState {
user: user.clone(),
daemon: None,
remotes: HashMap::new(),
},
option: option.clone(),
controller: SignerDaemonController {
crdt_event: CrdtEventController::new(core.clone()),
crdt_adapter: CrdtAdapterController::new(core.clone()),
remote: RemoteController::new(core.clone()),
},
store: SignerDaemonStore {
chat: ChatStore::new(core.clone()),
message: MessageStore::new(core.clone()),
server: ServerStore::new(core.clone()),
user: UserStore::new(core.clone(), option.clone()),
},
};
daemon.start().await
}
pub async fn from_sfs(
user: &SignerUser,
sfs: &SignerFsStore,
) -> crate::DaemonResult<Arc<RwLock<Self>>> {
SignerDaemon::from_path(
user,
sfs.root.join(&user.public.pub_key).to_str().unwrap(),
"user.json",
)
.await
}
pub(crate) async fn start(self) -> crate::DaemonResult<Arc<RwLock<Self>>> {
let daemon = Arc::new(RwLock::new(self));
{
let daemon_lock = daemon.clone();
let daemon = &mut *daemon.write().await;
daemon.state.daemon = Some(daemon_lock);
daemon.controller.crdt_adapter.apply_all().await?;
daemon
.controller
.remote
.reconcile(&mut daemon.state)
.await?;
daemon.sync_user_public().await?;
}
Ok(daemon)
}
pub async fn finalize(&mut self) -> crate::DaemonResult<()> {
tracing::info!("closing database {}", self.state.user.public.pub_key);
for i in self.state.remotes.values_mut() {
i.close().await;
}
self.core.db.close_by_ref().await?;
Ok(())
}
}