enigma_node_types/
relay.rs

1use base64::engine::general_purpose::STANDARD;
2use base64::Engine;
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6use crate::error::{EnigmaNodeTypesError, Result};
7use crate::user_id::UserId;
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
10#[serde(deny_unknown_fields)]
11pub struct RelayEnvelope {
12    pub id: Uuid,
13    pub to: UserId,
14    pub from: Option<UserId>,
15    pub created_at_ms: u64,
16    pub expires_at_ms: Option<u64>,
17    pub kind: RelayKind,
18}
19
20impl RelayEnvelope {
21    pub fn validate(&self) -> Result<()> {
22        if self.created_at_ms == 0 {
23            return Err(EnigmaNodeTypesError::InvalidField("created_at_ms"));
24        }
25        if let Some(expires) = self.expires_at_ms {
26            if expires <= self.created_at_ms {
27                return Err(EnigmaNodeTypesError::InvalidField("expires_at_ms"));
28            }
29        }
30        match &self.kind {
31            RelayKind::OpaqueMessage(message) => {
32                validate_blob(&message.blob_b64)?;
33                if let Some(ct) = &message.content_type {
34                    if ct.len() > 128 {
35                        return Err(EnigmaNodeTypesError::InvalidField("content_type"));
36                    }
37                }
38            }
39            RelayKind::OpaqueSignaling(signaling) => {
40                validate_blob(&signaling.blob_b64)?;
41            }
42            RelayKind::OpaqueAttachmentChunk(chunk) => {
43                validate_blob(&chunk.blob_b64)?;
44            }
45        }
46        Ok(())
47    }
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
51pub enum RelayKind {
52    OpaqueMessage(OpaqueMessage),
53    OpaqueSignaling(OpaqueSignaling),
54    OpaqueAttachmentChunk(OpaqueAttachmentChunk),
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
58#[serde(deny_unknown_fields)]
59pub struct OpaqueMessage {
60    pub blob_b64: String,
61    pub content_type: Option<String>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
65#[serde(deny_unknown_fields)]
66pub struct OpaqueSignaling {
67    pub blob_b64: String,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
71#[serde(deny_unknown_fields)]
72pub struct OpaqueAttachmentChunk {
73    pub blob_b64: String,
74    pub attachment_id: Uuid,
75    pub index: u32,
76    pub total: Option<u32>,
77}
78
79fn validate_blob(blob_b64: &str) -> Result<()> {
80    let decoded = STANDARD.decode(blob_b64.as_bytes())?;
81    if decoded.is_empty() {
82        return Err(EnigmaNodeTypesError::InvalidBase64);
83    }
84    Ok(())
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
88#[serde(deny_unknown_fields)]
89pub struct RelayPushRequest {
90    pub envelopes: Vec<RelayEnvelope>,
91}
92
93impl RelayPushRequest {
94    pub fn validate(&self) -> Result<()> {
95        for envelope in &self.envelopes {
96            envelope.validate()?;
97        }
98        Ok(())
99    }
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
103#[serde(deny_unknown_fields)]
104pub struct RelayPushResponse {
105    pub accepted: usize,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
109#[serde(deny_unknown_fields)]
110pub struct RelayPullResponse {
111    pub envelopes: Vec<RelayEnvelope>,
112    pub next_cursor: Option<String>,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
116#[serde(deny_unknown_fields)]
117pub struct RelayAckRequest {
118    pub ids: Vec<Uuid>,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
122#[serde(deny_unknown_fields)]
123pub struct RelayAckResponse {
124    pub removed: usize,
125}