signer-daemon 0.2.3

Signer daemon package.
Documentation
use serde::{Deserialize, Serialize};
use signer_core::{SignerCrypted, SignerSigned, SignerUser};

use crate::{SignerDaemon, reqwest_auth::ReqwestAuth};

use super::MessageVO;

#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "poem-openapi", derive(poem_openapi::Object))]
pub struct Envelope {
  pub data: SignerSigned<SignerCrypted<EnvelopeInner>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "poem-openapi", derive(poem_openapi::Object))]
pub struct EnvelopeInner {
  pub destinations: Vec<String>,
  pub message: MessageVO,
}

/**

 * 包含消息的信封
 * 用户通过解包信封获取实际上的消息结构体
 * 然后通过 CRDT Controller 对消息结构体进行处理
 * 将其写入到本地数据库,以供 View 层展示
 */
impl Envelope {
  pub async fn create(
    daemon: &SignerDaemon,
    message: &MessageVO,
    destinations: Vec<String>,
  ) -> anyhow::Result<Self> {
    let user = daemon.user.lock().await.clone();
    let inner = EnvelopeInner {
      destinations,
      message: message.clone(),
    };
    let inner = SignerCrypted::create(
      &user,
      &inner.message.chat.chat_key(daemon).await?,
      inner.clone(),
    )?;
    let inner = SignerSigned::from_value(&user, &inner)?;
    Ok(Self { data: inner })
  }

  pub fn open(&self, user: &SignerUser) -> anyhow::Result<EnvelopeInner> {
    let inner = self.data.verify_to_value()?;
    let inner = inner.decrypt(user)?;
    Ok(inner)
  }

  pub async fn send(&self, user: &SignerUser) -> anyhow::Result<()> {
    let inner = self.data.verify_to_value()?.decrypt(user)?;

    let client = reqwest::Client::new();

    for destination in inner.destinations {
      client
        .post(&format!("{}/envelopes", &destination))
        .with_signer_auth(&user)
        .json(&serde_json::json!({
          "data": vec![self.clone()],
        }))
        .send()
        .await?
        .error_for_status()?;
    }

    Ok(())
  }

  pub fn sender(&self) -> anyhow::Result<String> {
    Ok(self.data.verify_to_value()?.sender_pub_key)
  }

  pub fn receiver(&self) -> anyhow::Result<String> {
    Ok(self.data.verify_to_value()?.receiver_pub_key)
  }
}

#[cfg(test)]
mod test {
    use crate::model::viewobject::PrivateChatVO;

  #[tokio::test]
  async fn test_envelope() -> anyhow::Result<()> {
    use signer_core::SignerUser;

    use crate::{
      SignerDaemon,
      model::viewobject::{ChatVO, Envelope, MessageContent, MessageVO},
    };

    let alice = SignerUser::generete("alice")?;
    let bob = SignerUser::generete("bob")?;

    let alice_peer = uuid::Uuid::new_v4().to_string();
    let bob_peer = uuid::Uuid::new_v4().to_string();

    let alice_daemon = SignerDaemon::from_memory(&alice, &alice_peer).await?;
    let bob_daemon = SignerDaemon::from_memory(&bob, &bob_peer).await?;

    let message = MessageContent::Text(format!("Hello Bob!"));

    let message_id = uuid::Uuid::new_v4().to_string();
    let message = MessageVO {
      chat: ChatVO::Private(PrivateChatVO {
        peers: vec![bob.public.pub_key.clone(), alice.public.pub_key.clone()],
      }),
      id: message_id.clone(),
      parent_id: None,
      parent_user_key: None,
      user_key: alice.public.pub_key.clone(),
      create_time: chrono::Utc::now().timestamp_millis(),
      receiver_keys: vec![bob.public.pub_key.clone()],
      content: message,
    };

    let addr = format!("http://localhost:8080/api/signer");

    alice_daemon.ping_server(&addr).await?;
    bob_daemon.ping_server(&addr).await?;

    let envelope =
      Envelope::create(&alice_daemon, &message, vec![addr.clone()]).await?;
    envelope.send(&alice).await?;
    bob_daemon.remote_pull_message(&addr).await?;

    let msgs = bob_daemon.list_message().await?;
    assert!(msgs.len() == 1);

    Ok(())
  }

  #[tokio::test]
  async fn test_envelope_send_to_client() -> anyhow::Result<()> {
    use signer_core::SignerUser;

    use crate::{
      SignerDaemon,
      model::viewobject::{ChatVO, Envelope, MessageContent, MessageVO},
    };

    let alice = SignerUser::generete("alice")?;
    let alice_daemon =
      SignerDaemon::from_memory(&alice, &uuid::Uuid::new_v4().to_string())
        .await?;

    let message = MessageContent::Text(format!("Hello Bob!"));
    let message_id = uuid::Uuid::new_v4().to_string();
    let message = MessageVO {
      chat: ChatVO::Private(PrivateChatVO {
        peers: vec![
          "flNGOqa6fbJ67M8EKunes6P88zB09gcr6nRFFs-lJDs=".to_string(),
          alice.public.pub_key.clone(),
        ],
      }),
      id: message_id.clone(),
      parent_id: None,
      parent_user_key: None,
      user_key: alice.public.pub_key.clone(),
      create_time: chrono::Utc::now().timestamp_millis(),
      receiver_keys: vec![
        "flNGOqa6fbJ67M8EKunes6P88zB09gcr6nRFFs-lJDs=".to_string(),
      ],
      content: message,
    };

    let envelope = Envelope::create(
      &alice_daemon,
      &message,
      vec!["http://localhost:8080/api/signer".to_string()],
    )
    .await?;
    envelope.send(&alice).await?;

    Ok(())
  }
}