authbeam/api/
relationships.rs

1use crate::database::Database;
2use crate::model::{DatabaseError, UserFollow, RelationshipStatus};
3use databeam::prelude::DefaultReturn;
4
5use axum::response::IntoResponse;
6use axum::{
7    extract::{Path, State},
8    Json,
9};
10use axum_extra::extract::cookie::CookieJar;
11
12/// Toggle following on the given user
13pub async fn follow_request(
14    jar: CookieJar,
15    Path(id): Path<String>,
16    State(database): State<Database>,
17) -> impl IntoResponse {
18    // get user from token
19    let auth_user = match jar.get("__Secure-Token") {
20        Some(c) => match database.get_profile_by_unhashed(c.value_trimmed()).await {
21            Ok(ua) => ua,
22            Err(e) => return Json(e.to_json()),
23        },
24        None => return Json(DatabaseError::NotAllowed.to_json()),
25    };
26
27    // check block status
28    let attempting_to_follow = match database.get_profile(&id).await {
29        Ok(ua) => ua,
30        Err(_) => {
31            return Json(DefaultReturn {
32                success: false,
33                message: DatabaseError::NotFound.to_string(),
34                payload: (),
35            })
36        }
37    };
38
39    let relationship = database
40        .get_user_relationship(&attempting_to_follow.id, &auth_user.id)
41        .await
42        .0;
43
44    if relationship == RelationshipStatus::Blocked {
45        // blocked users cannot follow the people who blocked them!
46        return Json(DefaultReturn {
47            success: false,
48            message: DatabaseError::NotAllowed.to_string(),
49            payload: (),
50        });
51    }
52
53    // return
54    match database
55        .toggle_user_follow(&mut UserFollow {
56            user: auth_user.id,
57            following: attempting_to_follow.id,
58        })
59        .await
60    {
61        Ok(_) => Json(DefaultReturn {
62            success: true,
63            message: "Follow toggled".to_string(),
64            payload: (),
65        }),
66        Err(e) => Json(e.to_json()),
67    }
68}
69
70/// Send/accept a friend request to/from another user
71pub async fn friend_request(
72    jar: CookieJar,
73    Path(id): Path<String>,
74    State(database): State<Database>,
75) -> impl IntoResponse {
76    // get user from token
77    let auth_user = match jar.get("__Secure-Token") {
78        Some(c) => match database.get_profile_by_unhashed(c.value_trimmed()).await {
79            Ok(ua) => ua,
80            Err(e) => return Json(e.to_json()),
81        },
82        None => return Json(DatabaseError::NotAllowed.to_json()),
83    };
84
85    // ...
86    let other_user = match database.get_profile(&id).await {
87        Ok(ua) => ua,
88        Err(_) => {
89            return Json(DefaultReturn {
90                success: false,
91                message: DatabaseError::NotFound.to_string(),
92                payload: None,
93            })
94        }
95    };
96
97    // get current relationship
98    let current = database
99        .get_user_relationship(&auth_user.id, &other_user.id)
100        .await;
101
102    if current.0 == RelationshipStatus::Blocked && auth_user.id != current.1 {
103        // cannot change relationship if we're blocked and we aren't the user that did the blocking
104        return Json(DefaultReturn {
105            success: false,
106            message: DatabaseError::NotAllowed.to_string(),
107            payload: None,
108        });
109    }
110
111    let current = current.0;
112
113    // return
114    if current == RelationshipStatus::Unknown {
115        // send request
116        match database
117            .set_user_relationship_status(
118                &auth_user.id,
119                &other_user.id,
120                RelationshipStatus::Pending,
121                false,
122            )
123            .await
124        {
125            Ok(export) => {
126                return Json(DefaultReturn {
127                    success: true,
128                    message: "Friend request sent!".to_string(),
129                    payload: Some(export),
130                })
131            }
132            Err(e) => {
133                return Json(DefaultReturn {
134                    success: false,
135                    message: e.to_string(),
136                    payload: None,
137                })
138            }
139        }
140    } else if current == RelationshipStatus::Pending {
141        // accept request
142        match database
143            .set_user_relationship_status(
144                &auth_user.id,
145                &other_user.id,
146                RelationshipStatus::Friends,
147                false,
148            )
149            .await
150        {
151            Ok(export) => {
152                return Json(DefaultReturn {
153                    success: true,
154                    message: "Friend request accepted!".to_string(),
155                    payload: Some(export),
156                })
157            }
158            Err(e) => {
159                return Json(DefaultReturn {
160                    success: false,
161                    message: e.to_string(),
162                    payload: None,
163                })
164            }
165        }
166    } else {
167        // no clue, remove friendship?
168        match database
169            .set_user_relationship_status(
170                &auth_user.id,
171                &other_user.id,
172                RelationshipStatus::Unknown,
173                false,
174            )
175            .await
176        {
177            Ok(export) => {
178                return Json(DefaultReturn {
179                    success: true,
180                    message: "Friendship removed".to_string(),
181                    payload: Some(export),
182                })
183            }
184            Err(e) => {
185                return Json(DefaultReturn {
186                    success: false,
187                    message: e.to_string(),
188                    payload: None,
189                })
190            }
191        }
192    }
193}
194
195/// Block another user
196pub async fn block_request(
197    jar: CookieJar,
198    Path(id): Path<String>,
199    State(database): State<Database>,
200) -> impl IntoResponse {
201    // get user from token
202    let auth_user = match jar.get("__Secure-Token") {
203        Some(c) => match database.get_profile_by_unhashed(c.value_trimmed()).await {
204            Ok(ua) => ua,
205            Err(e) => return Json(e.to_json()),
206        },
207        None => return Json(DatabaseError::NotAllowed.to_json()),
208    };
209
210    // ...
211    let other_user = match database.get_profile(&id).await {
212        Ok(ua) => ua,
213        Err(_) => {
214            return Json(DefaultReturn {
215                success: false,
216                message: DatabaseError::NotFound.to_string(),
217                payload: None,
218            })
219        }
220    };
221
222    // get current relationship
223    let current = database
224        .get_user_relationship(&auth_user.id, &other_user.id)
225        .await;
226
227    if current.0 == RelationshipStatus::Blocked && auth_user.id != current.1 {
228        // cannot change relationship if we're blocked and we aren't the user that did the blocking
229        return Json(DefaultReturn {
230            success: false,
231            message: DatabaseError::NotAllowed.to_string(),
232            payload: None,
233        });
234    }
235
236    // force unfollow
237    if let Err(e) = database
238        .force_remove_user_follow(&mut UserFollow {
239            user: auth_user.id.to_string(),
240            following: other_user.id.to_string(),
241        })
242        .await
243    {
244        return Json(e.to_json());
245    }
246
247    if let Err(e) = database
248        .force_remove_user_follow(&mut UserFollow {
249            user: other_user.id.to_string(),
250            following: auth_user.id.to_string(),
251        })
252        .await
253    {
254        return Json(e.to_json());
255    }
256
257    // return
258    match database
259        .set_user_relationship_status(
260            &auth_user.id,
261            &other_user.id,
262            RelationshipStatus::Blocked,
263            false,
264        )
265        .await
266    {
267        Ok(export) => {
268            return Json(DefaultReturn {
269                success: true,
270                message: "User blocked!".to_string(),
271                payload: Some(export),
272            })
273        }
274        Err(e) => return Json(e.to_json()),
275    }
276}
277
278/// Remove relationship with another user
279pub async fn delete_request(
280    jar: CookieJar,
281    Path(id): Path<String>,
282    State(database): State<Database>,
283) -> impl IntoResponse {
284    // get user from token
285    let auth_user = match jar.get("__Secure-Token") {
286        Some(c) => match database.get_profile_by_unhashed(c.value_trimmed()).await {
287            Ok(ua) => ua,
288            Err(e) => return Json(e.to_json()),
289        },
290        None => return Json(DatabaseError::NotAllowed.to_json()),
291    };
292
293    // ...
294    let other_user = match database.get_profile(&id).await {
295        Ok(ua) => ua,
296        Err(_) => {
297            return Json(DefaultReturn {
298                success: false,
299                message: DatabaseError::NotFound.to_string(),
300                payload: None,
301            })
302        }
303    };
304
305    // get current relationship
306    let current = database
307        .get_user_relationship(&auth_user.id, &other_user.id)
308        .await;
309
310    if current.0 == RelationshipStatus::Blocked && auth_user.id != current.1 {
311        // cannot remove relationship if we're blocked and we aren't the user that did the blocking
312        return Json(DefaultReturn {
313            success: false,
314            message: DatabaseError::NotAllowed.to_string(),
315            payload: None,
316        });
317    }
318
319    // return
320    match database
321        .set_user_relationship_status(
322            &auth_user.id,
323            &other_user.id,
324            RelationshipStatus::Unknown,
325            false,
326        )
327        .await
328    {
329        Ok(export) => {
330            return Json(DefaultReturn {
331                success: true,
332                message: "Relationship removed!".to_string(),
333                payload: Some(export),
334            })
335        }
336        Err(e) => return Json(e.to_json()),
337    }
338}