relay_actions/actions/
inbox.rs1use serde::Serialize;
2
3use crate::{
4 actions::Action, cache::CacheManager, sign::sign_request, sinks::progress::ProgressSink,
5 storage::Storage,
6};
7use relay_lib::prelude::{Address, InboxId};
8
9pub struct Inbox {
10 pub address: Address,
11}
12
13impl Action for Inbox {
14 type Output = InboxId;
15
16 fn execute(
17 &self,
18 storage: &mut Storage,
19 cache: &mut CacheManager,
20 progress: &mut dyn ProgressSink,
21 ) -> Self::Output {
22 if self.address.inbox().is_none() {
23 progress.abort("Cannot claim an empty inbox.");
24 }
25
26 let inbox: InboxId = self.address.inbox().unwrap().clone();
27 let mut base_address = self.address.clone();
28 base_address.inbox = None;
29
30 progress.step(
31 &format!("Checking existing identity for `{}`", base_address),
32 "Checked existing identity",
33 );
34
35 let identity = match storage.root.get_identity(&base_address) {
36 Some(identity) => {
37 if identity.inboxes.contains(&inbox) {
38 progress.abort(&format!(
39 "Inbox `{}` already exists locally.",
40 inbox.canonical()
41 ));
42 }
43 identity.clone()
44 }
45 None => {
46 progress.abort(&format!(
47 "No identity found for `{}`. Claim the base identity first.",
48 base_address
49 ));
50 }
51 };
52
53 progress.step("Fetching records", "Fetched records");
54 let agent_record = cache
55 .records
56 .agent(&mut cache.agents, self.address.agent())
57 .unwrap_or_else(|e| {
58 progress.error(&format!("{:#?}", e));
59 progress.abort("Failed to fetch agent record");
60 });
61
62 progress.step("Requesting inbox claim", "Inbox claim requested");
63 let request = sign_request(
64 &base_address.canonical(),
65 ClaimInboxReq {
66 id: inbox.canonical().to_string(),
67 },
68 &agent_record,
69 &identity.signing_key(),
70 )
71 .unwrap_or_else(|e| {
72 progress.error(&format!("{:#?}", e));
73 progress.abort("Failed to sign inbox claim request");
74 });
75
76 let resp = reqwest::blocking::Client::new()
77 .post(
78 cache
79 .agents
80 .url(self.address.agent())
81 .unwrap_or_else(|e| {
82 progress.error(&format!("{:#?}", e));
83 progress.abort("Failed to fetch Agent information");
84 })
85 .join("/user/claim/inbox")
86 .unwrap_or_else(|e| {
87 progress.error(&format!("{:#?}", e));
88 progress.abort("Failed to construct inbox claim URL");
89 }),
90 )
91 .json(&request)
92 .send()
93 .unwrap_or_else(|e| {
94 progress.error(&format!("{:#?}", e));
95 progress.abort("Failed to send inbox claim request");
96 });
97
98 if resp.status().as_u16() == 409 {
99 progress.step("Inbox already claimed", "Inbox already claimed");
100
101 storage
102 .root
103 .identities
104 .get_mut(&base_address)
105 .unwrap_or_else(|| {
106 progress.abort(&format!(
107 "No identity found for `{}`. This should not happen as it was checked earlier.",
108 base_address
109 ));
110 })
111 .inboxes
112 .push(inbox.clone());
113
114 progress.done();
115 progress.arrow(&format!(
116 "Inbox `{}` was already claimed and added locally",
117 self.address
118 ));
119 return self.address.inbox().unwrap().clone();
120 }
121
122 if !resp.status().is_success() {
123 progress.abort(&format!(
124 "Failed to claim inbox. HTTP {}",
125 resp.status().as_u16()
126 ));
127 }
128
129 progress.step("Storing inbox locally", "Inbox stored locally");
130 storage
131 .root
132 .identities
133 .get_mut(&base_address)
134 .unwrap()
135 .inboxes
136 .push(inbox.clone());
137
138 progress.done();
139 self.address.inbox().unwrap().clone()
140 }
141}
142
143#[derive(Serialize)]
144struct ClaimInboxReq {
145 pub id: String,
146}