Skip to main content

relay_actions/actions/
inbox.rs

1use 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}