1use anyhow::Result;
2use async_trait::async_trait;
3use futures::stream::BoxStream;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
7#[serde(rename_all = "camelCase")]
8pub struct DeviceCapabilities {
9 pub can_host: bool,
10 pub can_sync: bool,
11 pub read_only: bool,
12}
13
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct Device {
17 pub app_tag: Option<String>,
18 pub device_id: String,
19 pub user_id: Option<String>,
20 pub device_name: String,
21 pub platform_type: Option<String>,
22 pub capabilities: Option<DeviceCapabilities>,
23 pub session_id: Option<String>,
24 pub node_id: Option<String>,
25 pub tag: Option<String>,
26 pub kind: Option<String>,
27 pub metadata: Option<String>,
28 pub online: bool,
29 pub ticket: Option<String>,
30 pub last_seen_at: Option<serde_json::Value>,
31 pub expires_at: Option<serde_json::Value>,
32 pub created_at: Option<serde_json::Value>,
33 pub updated_at: Option<serde_json::Value>,
34 #[serde(default)]
38 pub excluded_peers: Vec<String>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42#[serde(rename_all = "camelCase")]
43pub struct E2eeInfo {
44 pub session_key_id: String,
45 pub cipher_text: String,
46 pub iv: String,
47 pub salt: String,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
51#[serde(rename_all = "camelCase")]
52pub struct SignalingSession {
53 pub connection_id: String,
54 pub initiator: String,
55 pub target: String,
56 pub initiator_device_id: String,
57 pub target_device_id: String,
58 pub connection_type: Option<String>,
59 pub offer: Option<serde_json::Value>,
60 pub offer_e2ee: Option<E2eeInfo>,
61 pub answer: Option<serde_json::Value>,
62 pub answer_e2ee: Option<E2eeInfo>,
63 #[serde(default)]
64 pub ice_candidates: Vec<serde_json::Value>,
65 pub initiator_node_id: Option<String>,
66 pub target_node_id: Option<String>,
67 pub initiator_endpoint_addr: Option<String>,
68 pub target_endpoint_addr: Option<String>,
69 pub intent: Option<String>,
70 pub app_tag: Option<String>,
71 pub created_at: Option<i64>,
72 pub expires_at: Option<i64>,
73 pub state: String,
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
77#[serde(tag = "type", rename_all = "camelCase")]
78pub enum SessionEvent {
79 Added {
80 session: SignalingSession,
81 },
82 Modified {
83 session: SignalingSession,
84 },
85 Removed {
86 #[serde(rename = "sessionId")]
87 session_id: String,
88 },
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
92#[serde(tag = "type", rename_all = "camelCase")]
93pub enum DeviceEvent {
94 Added {
95 device: Device,
96 },
97 Modified {
98 device: Device,
99 },
100 Removed {
101 #[serde(rename = "deviceId")]
102 device_id: String,
103 },
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
107#[serde(rename_all = "camelCase")]
108pub struct SignalingEnvelope {
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub app_tag: Option<String>,
111 pub sender_id: String,
112 pub target_id: String,
113 pub payload: String,
114 pub state: Option<String>,
115 pub reply_payload: Option<String>,
116 pub timestamp: i64,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub sender_user_id: Option<String>,
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub target_user_id: Option<String>,
121 #[serde(skip_serializing_if = "Option::is_none")]
122 pub expires_at: Option<i64>,
123}
124
125#[cfg(target_arch = "wasm32")]
126pub trait SendSyncBound {}
127#[cfg(target_arch = "wasm32")]
128impl<T> SendSyncBound for T {}
129
130#[cfg(not(target_arch = "wasm32"))]
131pub trait SendSyncBound: Send + Sync {}
132#[cfg(not(target_arch = "wasm32"))]
133impl<T: Send + Sync> SendSyncBound for T {}
134
135#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
136#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
137pub trait SignalingBackend: SendSyncBound {
138 async fn update_presence(
139 &self,
140 user_id: &str,
141 local_node_id: &str,
142 ticket_str: &str,
143 is_online: bool,
144 name: &str,
145 ttl_ms: u64,
146 metadata: Option<&str>,
147 ) -> Result<()>;
148
149 async fn set_offline(&self, user_id: &str, local_node_id: &str) -> Result<()>;
150
151 async fn update_live_presence(
157 &self,
158 _user_id: &str,
159 _local_node_id: &str,
160 _ticket_str: &str,
161 _name: &str,
162 _metadata: Option<&str>,
163 ) -> Result<()> {
164 #[cfg(target_arch = "wasm32")]
165 return Ok(());
166
167 #[cfg(not(target_arch = "wasm32"))]
168 anyhow::bail!("native signaling backend does not implement RTDB live presence")
169 }
170
171 async fn set_live_presence_offline(&self, _user_id: &str, _local_node_id: &str) -> Result<()> {
174 #[cfg(target_arch = "wasm32")]
175 return Ok(());
176
177 #[cfg(not(target_arch = "wasm32"))]
178 anyhow::bail!("native signaling backend does not implement RTDB offline presence")
179 }
180
181 async fn update_device(
182 &self,
183 user_id: &str,
184 device_id: &str,
185 device_name: Option<&str>,
186 capabilities: Option<DeviceCapabilities>,
187 metadata: Option<&str>,
188 ) -> Result<()>;
189
190 async fn delete_device(&self, user_id: &str, device_id: &str) -> Result<()>;
191
192 async fn set_excluded_peers(
195 &self,
196 user_id: &str,
197 local_node_id: &str,
198 excluded_peers: &[String],
199 ) -> Result<()>;
200
201 async fn search_devices(
202 &self,
203 user_id: &str,
204 exclude_node_id: Option<&str>,
205 ) -> Result<Vec<Device>>;
206
207 async fn list_devices(
208 &self,
209 user_id: &str,
210 exclude_node_id: Option<&str>,
211 ) -> Result<Vec<Device>>;
212
213 async fn send_message(
214 &self,
215 sender_id: &str,
216 target_id: &str,
217 payload: &str,
218 state: Option<&str>,
219 reply_payload: Option<&str>,
220 ) -> Result<String>;
221
222 async fn subscribe_devices(
223 &self,
224 user_id: &str,
225 ) -> Result<BoxStream<'static, Result<Vec<DeviceEvent>>>>;
226
227 async fn create_session(&self, session: SignalingSession) -> Result<()>;
228
229 async fn update_session(&self, session_id: &str, update_data: serde_json::Value) -> Result<()>;
230
231 async fn subscribe_sessions(
232 &self,
233 local_device_id: &str,
234 ) -> Result<BoxStream<'static, Result<Vec<SessionEvent>>>>;
235}