signer-daemon 0.3.1

Signer daemon package.
Documentation
use serde::{Deserialize, Serialize};
use signer_core::{SignerCrypted, SignerSigned, SignerUser};
use signer_hub_kit::models::{
  OApiEnvelope, OApiSignerSignedOApiEnvelopeInner, PostEnvelopesRequest,
};

use crate::{OpenapiConfiguration, SignerDaemon};

use super::MessageVO;

#[derive(Debug, Clone, Serialize, Deserialize)]
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.state.user.clone();
    let inner = EnvelopeInner {
      destinations,
      message: message.clone(),
    };
    let inner = SignerCrypted::create(
      &user,
      &inner.message.chat.chat_key(&daemon.core).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)?;

    for destination in inner.destinations {
      let envelope = self.clone();
      signer_hub_kit::apis::default_api::api_signer_envelopes_post(
        &OpenapiConfiguration::new(user.clone(), destination).build(),
        PostEnvelopesRequest {
          data: vec![OApiEnvelope {
            data: Box::new(OApiSignerSignedOApiEnvelopeInner {
              sig: envelope.data.sig,
              msg: envelope.data.msg,
              pubkey: envelope.data.pubkey,
            }),
          }],
        },
      )
      .await?;
    }

    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, signer_remote::SignerRemote};

  #[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");
    let remote = SignerRemote::new(&addr);

    remote.ping(&alice).await?;
    remote.ping(&bob).await?;

    let envelope = Envelope::create(
      &*alice_daemon.read().await,
      &message,
      vec![addr.clone()],
    )
    .await?;
    envelope.send(&alice).await?;
    remote.pull_message(&mut *bob_daemon.write().await).await?;

    let msgs = bob_daemon.read().await.store.message.list().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.read().await,
      &message,
      vec!["http://localhost:8080".to_string()],
    )
    .await?;
    envelope.send(&alice).await?;

    Ok(())
  }
}