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