use std::fs;
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
use signer_core::{SignerUser, SignerUserPublic};
use crate::{
SignerDaemonCore, SignerDaemonOption, SignerDaemonState, UserStore,
entity::{server, user},
model::viewobject::UserVO,
};
use super::SignerDaemon;
impl SignerDaemon {
pub(crate) async fn sync_user_public(&mut self) -> crate::DaemonResult<()> {
let mut syncer = UserInfoSynchronizer::new(self.core.clone(), self.option.clone());
syncer.sync(&mut self.state).await?;
Ok(())
}
}
pub struct UserInfoSynchronizer {
core: SignerDaemonCore,
user_store: UserStore,
option: SignerDaemonOption,
}
impl UserInfoSynchronizer {
pub fn new(core: SignerDaemonCore, option: SignerDaemonOption) -> Self {
Self {
core: core.clone(),
user_store: UserStore::new(core.clone(), option.clone()),
option,
}
}
pub async fn sync(&mut self, state: &mut SignerDaemonState) -> crate::DaemonResult<()> {
let mem_up = &state.user.public;
let json_up = match &self.option.user_file_path {
Some(path) => {
let u: SignerUser = serde_json::from_str(&fs::read_to_string(path)?)?;
Some(u.public)
}
None => None,
};
let db_up: Option<SignerUserPublic> = match user::Entity::find()
.filter(user::Column::PubKey.eq(&mem_up.pub_key))
.one(&self.core.db)
.await?
{
None => None,
Some(m) => Some(serde_json::from_str(&m.user_public)?),
};
let mut up: SignerUserPublic = {
let mut up = mem_up.clone();
if let Some(json_up) = &json_up {
if json_up.update_time > up.update_time {
up = json_up.clone();
}
}
if let Some(db_up) = &db_up {
if db_up.update_time > up.update_time {
up = db_up.clone();
}
}
up
};
{
let servers = server::Entity::find().all(&self.core.db).await?;
let mut allow_endpoints = servers.into_iter().map(|s| s.addr).collect::<Vec<String>>();
allow_endpoints.sort();
if allow_endpoints != up.allow_endpoints {
up.allow_endpoints = allow_endpoints;
up.update_time = chrono::Utc::now().timestamp_millis();
}
}
{
if up.update_time > mem_up.update_time {
tracing::debug!("已更新内存中的用户信息");
state.user.public = up.clone();
}
if let Some(json_up) = json_up {
if up.update_time > json_up.update_time {
tracing::debug!("已更新配置文件中的用户信息");
let p = self.option.user_file_path.as_ref().unwrap();
let mut u: SignerUser = serde_json::from_str(&fs::read_to_string(p)?)?;
u.public = up.clone();
fs::write(p, serde_json::to_string(&u)?)?;
}
}
let db_need_update = match db_up {
None => true,
Some(db_up) => up.update_time > db_up.update_time,
};
if db_need_update {
tracing::debug!("已更新数据库中的用户信息");
self.user_store
.put(state, UserVO::from_signer_user(state.user.clone()).await?)
.await?;
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::model::viewobject::ServerVO;
use super::*;
use tokio;
#[tokio::test]
async fn test_sync_user_public() -> crate::DaemonResult<()> {
let random_user = SignerUser::generete("test_user").unwrap();
let daemon = SignerDaemon::from_memory(&random_user, "test").await?;
let daemon_write = &mut *daemon.write().await;
daemon_write
.store
.server
.put(
&mut daemon_write.state,
ServerVO {
addr: "http://localhost:8080".into(),
enable: true,
limitation_set: "".into(),
},
)
.await?;
let mut syncer =
UserInfoSynchronizer::new(daemon_write.core.clone(), daemon_write.option.clone());
syncer.sync(&mut daemon_write.state).await?;
assert!(
daemon_write.state.user.public.update_time > 0,
"用户信息未正确更新"
);
let db_user = user::Entity::find()
.filter(user::Column::PubKey.eq(&daemon_write.state.user.public.pub_key))
.one(&daemon_write.core.db)
.await?
.expect("数据库中未找到用户信息");
let db_user_public: SignerUserPublic = serde_json::from_str(&db_user.user_public)?;
assert_eq!(
db_user_public.update_time, daemon_write.state.user.public.update_time,
"数据库中的 update_time 与内存中的不一致"
);
Ok(())
}
}