rb/routing/pages/
settings.rs

1use reva_axum::Template;
2use axum::response::IntoResponse;
3use axum::{
4    extract::{State, Query},
5    response::Html,
6};
7use axum_extra::extract::CookieJar;
8
9use authbeam::model::{IpBlock, Item, Profile, Transaction};
10
11use crate::config::Config;
12use crate::database::Database;
13use crate::model::{DatabaseError, RelationshipStatus};
14use crate::ToHtml;
15
16use super::{clean_metadata_short, NotificationsQuery};
17
18#[derive(Template)]
19#[template(path = "settings/account.html")]
20struct AccountSettingsTemplate {
21    config: Config,
22    lang: langbeam::LangFile,
23    profile: Option<Box<Profile>>,
24    unread: usize,
25    notifs: usize,
26    metadata: String,
27    relationships: Vec<(Box<Profile>, RelationshipStatus)>,
28    ipblocks: Vec<IpBlock>,
29    user: Box<Profile>,
30    viewing_other_profile: bool,
31}
32
33/// GET /settings
34pub async fn account_settings(
35    jar: CookieJar,
36    State(database): State<Database>,
37    Query(props): Query<NotificationsQuery>,
38) -> impl IntoResponse {
39    let auth_user = match jar.get("__Secure-Token") {
40        Some(c) => match database
41            .auth
42            .get_profile_by_unhashed(c.value_trimmed())
43            .await
44        {
45            Ok(ua) => ua,
46            Err(_) => return Html(DatabaseError::NotAllowed.to_html(database)),
47        },
48        None => return Html(DatabaseError::NotAllowed.to_html(database)),
49    };
50
51    let unread = database.get_inbox_count_by_recipient(&auth_user.id).await;
52
53    let notifs = database
54        .auth
55        .get_notification_count_by_recipient(&auth_user.id)
56        .await;
57
58    let user = if props.profile.is_empty() {
59        auth_user.clone()
60    } else {
61        match database.get_profile(props.profile.clone()).await {
62            Ok(ua) => ua,
63            Err(e) => return Html(e.to_html(database)),
64        }
65    };
66
67    let viewing_other_profile =
68        (props.profile.is_empty() == false) && (props.profile != auth_user.id);
69
70    let is_helper = {
71        let group = match database.auth.get_group_by_id(auth_user.group).await {
72            Ok(g) => g,
73            Err(_) => return Html(DatabaseError::Other.to_html(database)),
74        };
75
76        group.permissions.check_helper()
77    };
78
79    if viewing_other_profile && !is_helper {
80        // we cannot view the settings of other users if we are not a helper
81        return Html(DatabaseError::NotAllowed.to_html(database));
82    }
83
84    let relationships = match database
85        .auth
86        .get_user_relationships_of_status(&user.id, RelationshipStatus::Blocked)
87        .await
88    {
89        Ok(r) => r,
90        Err(_) => Vec::new(),
91    };
92
93    let ipblocks = match database.auth.get_ipblocks(&user.id).await {
94        Ok(r) => r,
95        Err(_) => Vec::new(),
96    };
97
98    Html(
99        AccountSettingsTemplate {
100            config: database.config.clone(),
101            lang: database.lang(if let Some(c) = jar.get("net.rainbeam.langs.choice") {
102                c.value_trimmed()
103            } else {
104                ""
105            }),
106            metadata: clean_metadata_short(&user.metadata),
107            profile: Some(auth_user),
108            unread,
109            notifs,
110            relationships,
111            ipblocks,
112            user,
113            viewing_other_profile,
114        }
115        .render()
116        .unwrap(),
117    )
118}
119
120#[derive(Template)]
121#[template(path = "settings/profile.html")]
122struct ProfileSettingsTemplate {
123    config: Config,
124    lang: langbeam::LangFile,
125    profile: Option<Box<Profile>>,
126    unread: usize,
127    notifs: usize,
128    metadata: String,
129    user: Box<Profile>,
130    viewing_other_profile: bool,
131}
132
133/// GET /settings/profile
134pub async fn profile_settings(
135    jar: CookieJar,
136    State(database): State<Database>,
137    Query(props): Query<NotificationsQuery>,
138) -> impl IntoResponse {
139    let auth_user = match jar.get("__Secure-Token") {
140        Some(c) => match database
141            .auth
142            .get_profile_by_unhashed(c.value_trimmed())
143            .await
144        {
145            Ok(ua) => ua,
146            Err(_) => return Html(DatabaseError::NotAllowed.to_html(database)),
147        },
148        None => return Html(DatabaseError::NotAllowed.to_html(database)),
149    };
150
151    let unread = database.get_inbox_count_by_recipient(&auth_user.id).await;
152
153    let notifs = database
154        .auth
155        .get_notification_count_by_recipient(&auth_user.id)
156        .await;
157
158    let user = if props.profile.is_empty() {
159        auth_user.clone()
160    } else {
161        match database.get_profile(props.profile.clone()).await {
162            Ok(ua) => ua,
163            Err(e) => return Html(e.to_html(database)),
164        }
165    };
166
167    let viewing_other_profile =
168        (props.profile.is_empty() == false) && (props.profile != auth_user.id);
169
170    let is_helper = {
171        let group = match database.auth.get_group_by_id(auth_user.group).await {
172            Ok(g) => g,
173            Err(_) => return Html(DatabaseError::Other.to_html(database)),
174        };
175
176        group.permissions.check_helper()
177    };
178
179    if viewing_other_profile && !is_helper {
180        // we cannot view the settings of other users if we are not a helper
181        return Html(DatabaseError::NotAllowed.to_html(database));
182    }
183
184    Html(
185        ProfileSettingsTemplate {
186            config: database.config.clone(),
187            lang: database.lang(if let Some(c) = jar.get("net.rainbeam.langs.choice") {
188                c.value_trimmed()
189            } else {
190                ""
191            }),
192            metadata: clean_metadata_short(&user.metadata),
193            profile: Some(auth_user),
194            unread,
195            notifs,
196            user,
197            viewing_other_profile,
198        }
199        .render()
200        .unwrap(),
201    )
202}
203
204#[derive(Template)]
205#[template(path = "settings/theme.html")]
206struct ThemeSettingsTemplate {
207    config: Config,
208    lang: langbeam::LangFile,
209    profile: Option<Box<Profile>>,
210    unread: usize,
211    notifs: usize,
212    metadata: String,
213    user: Box<Profile>,
214    viewing_other_profile: bool,
215}
216
217/// GET /settings/theme
218pub async fn theme_settings(
219    jar: CookieJar,
220    State(database): State<Database>,
221    Query(props): Query<NotificationsQuery>,
222) -> impl IntoResponse {
223    let auth_user = match jar.get("__Secure-Token") {
224        Some(c) => match database
225            .auth
226            .get_profile_by_unhashed(c.value_trimmed())
227            .await
228        {
229            Ok(ua) => ua,
230            Err(_) => return Html(DatabaseError::NotAllowed.to_html(database)),
231        },
232        None => return Html(DatabaseError::NotAllowed.to_html(database)),
233    };
234
235    let unread = database.get_inbox_count_by_recipient(&auth_user.id).await;
236
237    let notifs = database
238        .auth
239        .get_notification_count_by_recipient(&auth_user.id)
240        .await;
241
242    let user = if props.profile.is_empty() {
243        auth_user.clone()
244    } else {
245        match database.get_profile(props.profile.clone()).await {
246            Ok(ua) => ua,
247            Err(e) => return Html(e.to_html(database)),
248        }
249    };
250
251    let viewing_other_profile =
252        (props.profile.is_empty() == false) && (props.profile != auth_user.id);
253
254    let is_helper = {
255        let group = match database.auth.get_group_by_id(auth_user.group).await {
256            Ok(g) => g,
257            Err(_) => return Html(DatabaseError::Other.to_html(database)),
258        };
259
260        group.permissions.check_helper()
261    };
262
263    if viewing_other_profile && !is_helper {
264        // we cannot view the settings of other users if we are not a helper
265        return Html(DatabaseError::NotAllowed.to_html(database));
266    }
267
268    Html(
269        ThemeSettingsTemplate {
270            config: database.config.clone(),
271            lang: database.lang(if let Some(c) = jar.get("net.rainbeam.langs.choice") {
272                c.value_trimmed()
273            } else {
274                ""
275            }),
276            metadata: clean_metadata_short(&user.metadata),
277            profile: Some(auth_user),
278            unread,
279            notifs,
280            user,
281            viewing_other_profile,
282        }
283        .render()
284        .unwrap(),
285    )
286}
287
288#[derive(Template)]
289#[template(path = "settings/privacy.html")]
290struct PrivacySettingsTemplate {
291    config: Config,
292    lang: langbeam::LangFile,
293    profile: Option<Box<Profile>>,
294    unread: usize,
295    notifs: usize,
296    metadata: String,
297    user: Box<Profile>,
298    viewing_other_profile: bool,
299}
300
301/// GET /settings/privacy
302pub async fn privacy_settings(
303    jar: CookieJar,
304    State(database): State<Database>,
305    Query(props): Query<NotificationsQuery>,
306) -> impl IntoResponse {
307    let auth_user = match jar.get("__Secure-Token") {
308        Some(c) => match database
309            .auth
310            .get_profile_by_unhashed(c.value_trimmed())
311            .await
312        {
313            Ok(ua) => ua,
314            Err(_) => return Html(DatabaseError::NotAllowed.to_html(database)),
315        },
316        None => return Html(DatabaseError::NotAllowed.to_html(database)),
317    };
318
319    let unread = database.get_inbox_count_by_recipient(&auth_user.id).await;
320
321    let notifs = database
322        .auth
323        .get_notification_count_by_recipient(&auth_user.id)
324        .await;
325
326    let user = if props.profile.is_empty() {
327        auth_user.clone()
328    } else {
329        match database.get_profile(props.profile.clone()).await {
330            Ok(ua) => ua,
331            Err(e) => return Html(e.to_html(database)),
332        }
333    };
334
335    let viewing_other_profile =
336        (props.profile.is_empty() == false) && (props.profile != auth_user.id);
337
338    let is_helper = {
339        let group = match database.auth.get_group_by_id(auth_user.group).await {
340            Ok(g) => g,
341            Err(_) => return Html(DatabaseError::Other.to_html(database)),
342        };
343
344        group.permissions.check_helper()
345    };
346
347    if viewing_other_profile && !is_helper {
348        // we cannot view the settings of other users if we are not a helper
349        return Html(DatabaseError::NotAllowed.to_html(database));
350    }
351
352    Html(
353        PrivacySettingsTemplate {
354            config: database.config.clone(),
355            lang: database.lang(if let Some(c) = jar.get("net.rainbeam.langs.choice") {
356                c.value_trimmed()
357            } else {
358                ""
359            }),
360            metadata: clean_metadata_short(&user.metadata),
361            profile: Some(auth_user),
362            unread,
363            notifs,
364            user,
365            viewing_other_profile,
366        }
367        .render()
368        .unwrap(),
369    )
370}
371
372#[derive(Template)]
373#[template(path = "settings/sessions.html")]
374struct SessionsSettingsTemplate {
375    config: Config,
376    lang: langbeam::LangFile,
377    profile: Option<Box<Profile>>,
378    unread: usize,
379    notifs: usize,
380    metadata: String,
381    tokens: String,
382    tokens_src: Vec<String>,
383    current_session: String,
384    user: Box<Profile>,
385    viewing_other_profile: bool,
386}
387
388/// GET /settings/sessions
389pub async fn sessions_settings(
390    jar: CookieJar,
391    State(database): State<Database>,
392    Query(props): Query<NotificationsQuery>,
393) -> impl IntoResponse {
394    let auth_user = match jar.get("__Secure-Token") {
395        Some(c) => match database
396            .auth
397            .get_profile_by_unhashed(c.value_trimmed())
398            .await
399        {
400            Ok(ua) => ua,
401            Err(_) => return Html(DatabaseError::NotAllowed.to_html(database)),
402        },
403        None => return Html(DatabaseError::NotAllowed.to_html(database)),
404    };
405
406    let unread = database.get_inbox_count_by_recipient(&auth_user.id).await;
407
408    let notifs = database
409        .auth
410        .get_notification_count_by_recipient(&auth_user.id)
411        .await;
412
413    let user = if props.profile.is_empty() {
414        auth_user.clone()
415    } else {
416        match database.get_profile(props.profile.clone()).await {
417            Ok(ua) => ua,
418            Err(e) => return Html(e.to_html(database)),
419        }
420    };
421
422    let viewing_other_profile =
423        (props.profile.is_empty() == false) && (props.profile != auth_user.id);
424
425    let is_helper = {
426        let group = match database.auth.get_group_by_id(auth_user.group).await {
427            Ok(g) => g,
428            Err(_) => return Html(DatabaseError::Other.to_html(database)),
429        };
430
431        group.permissions.check_helper()
432    };
433
434    if viewing_other_profile && !is_helper {
435        // we cannot view the settings of other users if we are not a helper
436        return Html(DatabaseError::NotAllowed.to_html(database));
437    }
438
439    Html(
440        SessionsSettingsTemplate {
441            config: database.config.clone(),
442            lang: database.lang(if let Some(c) = jar.get("net.rainbeam.langs.choice") {
443                c.value_trimmed()
444            } else {
445                ""
446            }),
447            metadata: clean_metadata_short(&user.metadata),
448            tokens: serde_json::to_string(&user.tokens).unwrap(),
449            tokens_src: user.tokens.clone(),
450            profile: Some(auth_user),
451            unread,
452            notifs,
453            user,
454            current_session: rainbeam_shared::hash::hash(
455                jar.get("__Secure-Token")
456                    .unwrap()
457                    .value_trimmed()
458                    .to_string(),
459            ),
460            viewing_other_profile,
461        }
462        .render()
463        .unwrap(),
464    )
465}
466
467#[derive(Template)]
468#[template(path = "settings/coins.html")]
469struct CoinsSettingsTemplate {
470    config: Config,
471    lang: langbeam::LangFile,
472    profile: Option<Box<Profile>>,
473    unread: usize,
474    notifs: usize,
475    metadata: String,
476    transactions: Vec<((Transaction, Option<Item>), Box<Profile>, Box<Profile>)>,
477    page: i32,
478    user: Box<Profile>,
479    viewing_other_profile: bool,
480}
481
482/// GET /settings/coins
483pub async fn coins_settings(
484    jar: CookieJar,
485    State(database): State<Database>,
486    Query(props): Query<NotificationsQuery>,
487) -> impl IntoResponse {
488    let auth_user = match jar.get("__Secure-Token") {
489        Some(c) => match database
490            .auth
491            .get_profile_by_unhashed(c.value_trimmed())
492            .await
493        {
494            Ok(ua) => ua,
495            Err(_) => return Html(DatabaseError::NotAllowed.to_html(database)),
496        },
497        None => return Html(DatabaseError::NotAllowed.to_html(database)),
498    };
499
500    let unread = database.get_inbox_count_by_recipient(&auth_user.id).await;
501
502    let notifs = database
503        .auth
504        .get_notification_count_by_recipient(&auth_user.id)
505        .await;
506
507    let user = if props.profile.is_empty() {
508        auth_user.clone()
509    } else {
510        match database.get_profile(props.profile.clone()).await {
511            Ok(ua) => ua,
512            Err(e) => return Html(e.to_html(database)),
513        }
514    };
515
516    let viewing_other_profile =
517        (props.profile.is_empty() == false) && (props.profile != auth_user.id);
518
519    let is_helper = {
520        let group = match database.auth.get_group_by_id(auth_user.group).await {
521            Ok(g) => g,
522            Err(_) => return Html(DatabaseError::Other.to_html(database)),
523        };
524
525        group.permissions.check_helper()
526    };
527
528    if viewing_other_profile && !is_helper {
529        // we cannot view the settings of other users if we are not a helper
530        return Html(DatabaseError::NotAllowed.to_html(database));
531    }
532
533    let transactions = match database
534        .auth
535        .get_participating_transactions_paginated(&user.id, props.page)
536        .await
537    {
538        Ok(t) => t,
539        Err(e) => return Html(e.to_string()),
540    };
541
542    Html(
543        CoinsSettingsTemplate {
544            config: database.config.clone(),
545            lang: database.lang(if let Some(c) = jar.get("net.rainbeam.langs.choice") {
546                c.value_trimmed()
547            } else {
548                ""
549            }),
550            metadata: clean_metadata_short(&user.metadata),
551            profile: Some(auth_user),
552            unread,
553            notifs,
554            user,
555            transactions,
556            page: props.page,
557            viewing_other_profile,
558        }
559        .render()
560        .unwrap(),
561    )
562}