signer-daemon 0.3.2

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

use crate::{
    HttpClient, HttpClientConfig, JsonTextResponse, OApiEnvelope,
    OApiSignerSignedOApiEnvelopeInner, PostEnvelopesRequest, SignerDaemon,
    model::viewobject::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>,
    ) -> crate::DaemonResult<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) -> crate::DaemonResult<EnvelopeInner> {
        let inner = self.data.verify_to_value()?;
        let inner = inner.decrypt(user)?;
        Ok(inner)
    }

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

        for destination in inner.destinations {
            let envelope = self.clone();
            let config = HttpClientConfig::new(user.clone(), destination);
            let client = HttpClient::new(config);

            let _response: JsonTextResponse = client
                .post(
                    "/api/envelopes",
                    &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) -> crate::DaemonResult<String> {
        Ok(self.data.verify_to_value()?.sender_pub_key)
    }

    pub fn receiver(&self) -> crate::DaemonResult<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() -> crate::DaemonResult<()> {
        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() -> crate::DaemonResult<()> {
        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(())
    }
}