mas_handlers/views/
app.rs

1// Copyright 2024 New Vector Ltd.
2// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
6
7use axum::{
8    extract::{Query, State},
9    response::{Html, IntoResponse},
10};
11use mas_axum_utils::{FancyError, cookies::CookieJar};
12use mas_router::{PostAuthAction, UrlBuilder};
13use mas_storage::{BoxClock, BoxRepository, BoxRng};
14use mas_templates::{AppContext, TemplateContext, Templates};
15use serde::Deserialize;
16
17use crate::{
18    BoundActivityTracker, PreferredLanguage,
19    session::{SessionOrFallback, load_session_or_fallback},
20};
21
22#[derive(Deserialize)]
23pub struct Params {
24    #[serde(default, flatten)]
25    action: Option<mas_router::AccountAction>,
26}
27
28#[tracing::instrument(name = "handlers.views.app.get", skip_all, err)]
29pub async fn get(
30    PreferredLanguage(locale): PreferredLanguage,
31    State(templates): State<Templates>,
32    activity_tracker: BoundActivityTracker,
33    State(url_builder): State<UrlBuilder>,
34    Query(Params { action }): Query<Params>,
35    mut repo: BoxRepository,
36    clock: BoxClock,
37    mut rng: BoxRng,
38    cookie_jar: CookieJar,
39) -> Result<impl IntoResponse, FancyError> {
40    let (cookie_jar, maybe_session) = match load_session_or_fallback(
41        cookie_jar, &clock, &mut rng, &templates, &locale, &mut repo,
42    )
43    .await?
44    {
45        SessionOrFallback::MaybeSession {
46            cookie_jar,
47            maybe_session,
48            ..
49        } => (cookie_jar, maybe_session),
50        SessionOrFallback::Fallback { response } => return Ok(response),
51    };
52
53    // TODO: keep the full path, not just the action
54    let Some(session) = maybe_session else {
55        return Ok((
56            cookie_jar,
57            url_builder.redirect(&mas_router::Login::and_then(
58                PostAuthAction::manage_account(action),
59            )),
60        )
61            .into_response());
62    };
63
64    activity_tracker
65        .record_browser_session(&clock, &session)
66        .await;
67
68    let ctx = AppContext::from_url_builder(&url_builder).with_language(locale);
69    let content = templates.render_app(&ctx)?;
70
71    Ok((cookie_jar, Html(content)).into_response())
72}
73
74/// Like `get`, but allow anonymous access.
75/// Used for a subset of the account management paths.
76/// Needed for e.g. account recovery.
77#[tracing::instrument(name = "handlers.views.app.get_anonymous", skip_all, err)]
78pub async fn get_anonymous(
79    PreferredLanguage(locale): PreferredLanguage,
80    State(templates): State<Templates>,
81    State(url_builder): State<UrlBuilder>,
82) -> Result<impl IntoResponse, FancyError> {
83    let ctx = AppContext::from_url_builder(&url_builder).with_language(locale);
84    let content = templates.render_app(&ctx)?;
85
86    Ok(Html(content).into_response())
87}