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
33pub 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 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
133pub 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 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
217pub 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 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
301pub 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 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
388pub 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 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
482pub 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 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}