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;
16#[cfg(feature = "scripting")]
17pub mod script;
18pub mod tag;
19#[cfg(feature = "scripting")]
20pub mod template;
21pub mod transaction;
22pub mod validation;
23
24pub async fn index(State(data): State<Arc<AppState>>, cookie_jar: CookieJar) -> impl IntoResponse {
25    let _script = br#"
26(module $commodity.wasm
27  (type (;0;) (func (param i64)))
28  (type (;1;) (func (param i32 i32)))
29  (type (;2;) (func))
30  (import "env" "set_frac" (func $set_frac (type 0)))
31  (func $process (type 2)
32    i64.const 15
33    call $set_frac)
34  (table (;0;) 1 1 funcref)
35  (memory (;0;) 17)
36  (global $__stack_pointer (mut i32) (i32.const 1048576))
37  (global (;1;) i32 (i32.const 1048602))
38  (global (;2;) i32 (i32.const 1048608))
39  (export "memory" (memory 0))
40  (export "process" (func $process))
41  (export "__data_end" (global 1))
42  (export "__heap_base" (global 2))
43  (data $.rodata (i32.const 1048576) "!test value to check page!"))
44"#;
45    let is_logged_in = match cookie_jar.get("access_token") {
46        Some(cookie) => crate::auth_keys::verify(cookie.value(), crate::token::TokenType::Access)
47            .await
48            .is_some(),
49        None => false,
50    };
51
52    // User name will be empty for index page since it doesn't require auth
53    let user_name = String::new();
54
55    let template = IndexTemplate {
56        fraction: data.frac,
57        is_logged_in,
58        user_name,
59        scripting_enabled: cfg!(feature = "scripting"),
60    };
61
62    HtmlTemplate(template)
63}
64
65#[derive(Template)]
66#[template(path = "pages/index.html")]
67struct IndexTemplate {
68    fraction: i64,
69    is_logged_in: bool,
70    user_name: String,
71    scripting_enabled: bool,
72}
73
74pub async fn file_table(
75    State(data): State<Arc<AppState>>,
76) -> Result<impl IntoResponse, StatusCode> {
77    let frac = State(&data).frac;
78    let template = FileTableTemplate {
79        files: list_s3(State(data)).await?,
80        frac,
81    };
82    Ok(HtmlTemplate(template))
83}
84
85#[derive(Template)]
86#[template(path = "components/file-table.html")]
87struct FileTableTemplate {
88    files: Vec<S3File>,
89    frac: i64,
90}
91
92pub async fn register() -> impl IntoResponse {
93    let template = RegisterTemplate {};
94    HtmlTemplate(template)
95}
96
97#[derive(Template)]
98#[template(path = "pages/register.html")]
99struct RegisterTemplate;
100
101pub async fn login() -> impl IntoResponse {
102    let template = LoginTemplate {};
103    HtmlTemplate(template)
104}
105
106#[derive(Template)]
107#[template(path = "pages/login.html")]
108struct LoginTemplate;
109
110/// A wrapper type that we'll use to encapsulate HTML parsed by askama into valid HTML for axum to serve.
111struct HtmlTemplate<T>(T);
112
113/// Allows us to convert Askama HTML templates into valid HTML for axum to serve in the response.
114impl<T> IntoResponse for HtmlTemplate<T>
115where
116    T: Template,
117{
118    fn into_response(self) -> Response {
119        // Attempt to render the template with askama
120        match self.0.render() {
121            // If we're able to successfully parse and aggregate the template, serve it
122            Ok(html) => Html(html).into_response(),
123            // If we're not, return an error or some bit of fallback HTML
124            Err(err) => (
125                StatusCode::INTERNAL_SERVER_ERROR,
126                format!("Failed to render template. Error: {err}"),
127            )
128                .into_response(),
129        }
130    }
131}