Skip to main content

web/
pages.rs

1use std::sync::Arc;
2
3use crate::AppState;
4
5use crate::files::{S3File, list_s3};
6use askama::Template;
7use axum::{
8    extract::State,
9    http::StatusCode,
10    response::{Html, IntoResponse, Response},
11};
12use axum_extra::extract::CookieJar;
13pub mod account;
14pub mod commodity;
15pub mod report;
16pub mod script;
17pub mod tag;
18pub mod template;
19pub mod transaction;
20pub mod validation;
21
22pub async fn index(cookie_jar: CookieJar) -> impl IntoResponse {
23    let is_logged_in = match cookie_jar.get("access_token") {
24        // A directory outage renders the logged-out page rather than failing:
25        // this only decides which navigation the index shows.
26        Some(cookie) => crate::auth_keys::verify(cookie.value(), crate::token::TokenType::Access)
27            .await
28            .is_ok_and(|details| details.is_some()),
29        None => false,
30    };
31
32    let template = IndexTemplate {
33        is_logged_in,
34        scripting_enabled: true,
35    };
36
37    HtmlTemplate(template)
38}
39
40#[derive(Template)]
41#[template(path = "pages/index.html")]
42struct IndexTemplate {
43    is_logged_in: bool,
44    scripting_enabled: bool,
45}
46
47pub async fn file_table(
48    State(data): State<Arc<AppState>>,
49) -> Result<impl IntoResponse, StatusCode> {
50    let frac = State(&data).frac;
51    let template = FileTableTemplate {
52        files: list_s3(State(data)).await?,
53        frac,
54    };
55    Ok(HtmlTemplate(template))
56}
57
58#[derive(Template)]
59#[template(path = "components/file-table.html")]
60struct FileTableTemplate {
61    files: Vec<S3File>,
62    frac: i64,
63}
64
65pub async fn register() -> impl IntoResponse {
66    let template = RegisterTemplate {};
67    HtmlTemplate(template)
68}
69
70#[derive(Template)]
71#[template(path = "pages/register.html")]
72struct RegisterTemplate;
73
74pub async fn login() -> impl IntoResponse {
75    let template = LoginTemplate {};
76    HtmlTemplate(template)
77}
78
79#[derive(Template)]
80#[template(path = "pages/login.html")]
81struct LoginTemplate;
82
83/// A wrapper type that we'll use to encapsulate HTML parsed by askama into valid HTML for axum to serve.
84struct HtmlTemplate<T>(T);
85
86/// Allows us to convert Askama HTML templates into valid HTML for axum to serve in the response.
87impl<T> IntoResponse for HtmlTemplate<T>
88where
89    T: Template,
90{
91    fn into_response(self) -> Response {
92        // Attempt to render the template with askama
93        match self.0.render() {
94            // If we're able to successfully parse and aggregate the template, serve it
95            Ok(html) => Html(html).into_response(),
96            // If we're not, return an error or some bit of fallback HTML
97            Err(err) => (
98                StatusCode::INTERNAL_SERVER_ERROR,
99                format!("Failed to render template. Error: {err}"),
100            )
101                .into_response(),
102        }
103    }
104}