repos
/ analytics-rust master

analytics-rust

mirror archived upstream

Single-binary self-hosted website analytics on Rust axum: collector API, dashboards, world map, and PDF reports.

analyticsaxumdockerrustself-hostedsqliteviteweb-analytics

2.2 KB · 66 lines · Rust Raw History
 1use axum::{
 2    http::StatusCode,
 3    response::{Html, IntoResponse, Response},
 4};
 5use chrono::Datelike;
 6
 7use crate::templates::{RequestCtx, UserCtx};
 8use crate::AppState;
 9
10/// Render a template to a String, with the standard page context injected.
11///
12/// `extra` is merged on top of the standard context. Templates expect
13/// `user`, `request`, `now`, `base_url`, `collector_id`, `collector_server`,
14/// `messages` to be present; callers supply page-specific fields like `page`
15/// via `extra`.
16///
17/// Returns the rendered body on success, or a 500 Response with the error
18/// already logged.
19pub fn render_to_string(
20    state: &AppState,
21    template: &str,
22    path: &str,
23    authed: bool,
24    extra: minijinja::Value,
25) -> Result<String, Response> {
26    let tmpl = state.env.get_template(template).map_err(|e| {
27        tracing::error!("template '{}': {}", template, e);
28        (StatusCode::INTERNAL_SERVER_ERROR, "template error").into_response()
29    })?;
30    tmpl.render(minijinja::context! {
31        user => UserCtx { is_authenticated: authed },
32        request => RequestCtx {
33            url: String::new(),
34            url_root: "/".to_string(),
35            base_url: String::new(),
36            path: path.to_string(),
37        },
38        now => minijinja::context! { year => chrono::Local::now().year() },
39        base_url => &state.config.base_url,
40        collector_id => state.config.proprium_id.map(|u| u.to_string()),
41        collector_server => &state.config.base_url,
42        messages => Vec::<()>::new(),
43        ..extra
44    })
45    .map_err(|e| {
46        tracing::error!("render '{}': {}", template, e);
47        (StatusCode::INTERNAL_SERVER_ERROR, "render error").into_response()
48    })
49}
50
51/// Convenience wrapper around `render_to_string` for HTML responses. Most
52/// page handlers want this; `render_to_string` is for callers that need the
53/// raw body (e.g. markdown downloads, PDF print templates).
54pub fn render(
55    state: &AppState,
56    template: &str,
57    path: &str,
58    authed: bool,
59    extra: minijinja::Value,
60) -> Response {
61    match render_to_string(state, template, path, authed, extra) {
62        Ok(body) => Html(body).into_response(),
63        Err(resp) => resp,
64    }
65}