pub mod crdt;
pub mod delta;
pub mod entity;
pub mod errors;
pub mod view;
pub use crdt::*;
pub use delta::*;
pub use entity::*;
pub use errors::*;
pub use sea_orm;
pub use view::*;
use chrono;
use sea_orm::{Database, DatabaseConnection, ConnectionTrait, DatabaseBackend, Statement};
use signer_core::{SignerKeys, SignerUser};
use signer_db::MigratorTrait;
use std::sync::Arc;
use tokio::sync::{broadcast, Mutex};
#[derive(Debug, Clone)]
pub enum CrdtMutate {
UserPut(view::UserVO),
UserDel(String), MessagePut(view::MessageVO),
MessageDel(crate::crdt::crdt_message::MessageKey),
ChatPut(view::ChatVO),
ChatDel(String), }
#[derive(Clone)]
pub struct SignerMeta {
pub keys: SignerKeys,
pub conn: DatabaseConnection,
pub event_bus: broadcast::Sender<CrdtMutate>,
_event_bus_dummy_receiver: Arc<Mutex<broadcast::Receiver<CrdtMutate>>>,
pub write_mutex: Arc<Mutex<()>>,
}
impl SignerMeta {
pub fn event_bus(&self) -> broadcast::Receiver<CrdtMutate> {
self.event_bus.subscribe()
}
async fn enable_wal_mode(conn: &DatabaseConnection) -> Result<(), ViewError> {
let stmt = Statement::from_string(
DatabaseBackend::Sqlite,
"PRAGMA journal_mode=WAL;".to_string(),
);
conn.execute(stmt).await?;
tracing::info!("已为数据库连接启用 WAL 模式,提高并发读写性能");
Ok(())
}
async fn wal_checkpoint(conn: &DatabaseConnection) -> Result<(), ViewError> {
let stmt = Statement::from_string(
DatabaseBackend::Sqlite,
"PRAGMA wal_checkpoint(full);".to_string(),
);
conn.execute(stmt).await?;
tracing::info!("已执行 WAL checkpoint,将 WAL 内容落库");
Ok(())
}
pub async fn get_current_user(&self) -> Result<SignerUser, ViewError> {
let user_vo = view::UserVO::get(self, &self.keys.pub_key)
.await?
.ok_or(ViewError::InvalidViewObjectError("用户不存在".to_string()))?;
let signed_user_public: signer_core::SignerSigned<signer_core::SignerUser> =
serde_json::from_str(&user_vo.signed_user_public)?;
let user_public = signed_user_public.verify_to_value()?;
Ok(user_public)
}
pub async fn from_mem(keys: SignerKeys) -> Result<Self, ViewError> {
let conn = Database::connect("sqlite::memory:").await?;
signer_db::Migrator::up(&conn, None).await?;
Self::enable_wal_mode(&conn).await?;
Self::wal_checkpoint(&conn).await?;
let (event_bus, rx) = broadcast::channel(100);
let _event_bus_dummy_receiver = Arc::new(Mutex::new(rx));
let meta = SignerMeta {
keys,
conn,
event_bus,
_event_bus_dummy_receiver,
write_mutex: Arc::new(Mutex::new(())),
};
if let Err(_) = meta.get_current_user().await {
let default_user = signer_core::SignerUser {
pub_key: meta.keys.pub_key.clone(),
update_time: chrono::Utc::now().timestamp(),
..Default::default()
};
let user_vo = view::UserVO::from_user_data(&meta.keys, &default_user).await?;
user_vo.put(&meta).await?;
}
Ok(meta)
}
pub async fn from_fs(path: &str) -> Result<Self, ViewError> {
if !std::fs::exists(path)? {
std::fs::create_dir_all(path)?;
}
let keys_path = format!("{}/signer_keys.json", path);
let keys_data = std::fs::read_to_string(&keys_path)?;
let keys: SignerKeys = serde_json::from_str(&keys_data)?;
let db_url = format!("sqlite://{}/signer.sqlite3?mode=rwc", path);
let conn = Database::connect(db_url).await?;
signer_db::Migrator::up(&conn, None).await?;
Self::enable_wal_mode(&conn).await?;
Self::wal_checkpoint(&conn).await?;
let (event_bus, rx) = broadcast::channel(100);
let _event_bus_dummy_receiver = Arc::new(Mutex::new(rx));
let meta = SignerMeta {
keys,
conn,
event_bus,
_event_bus_dummy_receiver,
write_mutex: Arc::new(Mutex::new(())),
};
if let Err(_) = meta.get_current_user().await {
let default_user = signer_core::SignerUser {
pub_key: meta.keys.pub_key.clone(),
update_time: chrono::Utc::now().timestamp(),
..Default::default()
};
let user_vo = view::UserVO::from_user_data(&meta.keys, &default_user).await?;
user_vo.put(&meta).await?;
}
Ok(meta)
}
pub fn save_fs(&self, path: &str) -> Result<(), ViewError> {
std::fs::create_dir_all(path)?;
let keys_path = format!("{}/signer_keys.json", path);
let keys_data = serde_json::to_string_pretty(&self.keys)?;
std::fs::write(keys_path, keys_data)?;
Ok(())
}
pub fn save_fs_raw_keys(keys: &SignerKeys, path: &str) -> Result<(), ViewError> {
std::fs::create_dir_all(path)?;
let keys_path = format!("{}/signer_keys.json", path);
let keys_data = serde_json::to_string_pretty(keys)?;
std::fs::write(keys_path, keys_data)?;
Ok(())
}
}