feat: Use a context struct to store global template context

This commit is contained in:
Ginger 2026-03-18 10:26:43 -04:00
parent f9d1f71343
commit 51b450c05c
No known key found for this signature in database
5 changed files with 82 additions and 45 deletions

View file

@ -9,6 +9,8 @@ use conduwuit_service::state;
use tower_http::set_header::SetResponseHeaderLayer; use tower_http::set_header::SetResponseHeaderLayer;
use tower_sec_fetch::SecFetchLayer; use tower_sec_fetch::SecFetchLayer;
use crate::pages::TemplateContext;
mod pages; mod pages;
type State = state::State; type State = state::State;
@ -40,8 +42,9 @@ impl IntoResponse for WebError {
struct Error { struct Error {
error: WebError, error: WebError,
status: StatusCode, status: StatusCode,
allow_indexing: bool, context: TemplateContext,
} }
let status = match &self { let status = match &self {
| Self::ValidationError(_) | Self::ValidationError(_)
| Self::BadRequest(_) | Self::BadRequest(_)
@ -54,9 +57,11 @@ impl IntoResponse for WebError {
let template = Error { let template = Error {
error: self, error: self,
status, status,
context: TemplateContext {
// Statically set false to prevent error pages from being indexed and to prevent // Statically set false to prevent error pages from being indexed and to prevent
// further errors if services.config is having issues. // further errors if services.config is having issues.
allow_indexing: false, allow_indexing: false,
},
}; };
if let Ok(body) = template.render() { if let Ok(body) = template.render() {

View file

@ -1,30 +1,24 @@
use askama::Template; use askama::Template;
use axum::{ use axum::{Router, extract::State, response::IntoResponse, routing::get};
Router,
extract::State,
response::{Html, IntoResponse},
routing::get,
};
use crate::WebError; use crate::{WebError, template};
pub(crate) fn build() -> Router<crate::State> { Router::new().route("/", get(index_handler)) } pub(crate) fn build() -> Router<crate::State> { Router::new().route("/", get(index_handler)) }
async fn index_handler( async fn index_handler(
State(services): State<crate::State>, State(services): State<crate::State>,
) -> Result<impl IntoResponse, WebError> { ) -> Result<impl IntoResponse, WebError> {
#[derive(Debug, Template)] template! {
#[template(path = "index.html.j2")] struct Index<'a> use "index.html.j2" {
struct Index<'a> {
server_name: &'a str, server_name: &'a str,
first_run: bool, first_run: bool
allow_indexing: bool, }
} }
let template = Index { Ok(Index::new(
server_name: services.globals.server_name().as_str(), &services,
first_run: services.firstrun.is_first_run(), services.globals.server_name().as_str(),
allow_indexing: services.config.index_page_allow_indexing, services.firstrun.is_first_run(),
}; )
Ok(Html(template.render()?)) .into_response())
} }

View file

@ -2,3 +2,51 @@ mod components;
pub(super) mod index; pub(super) mod index;
pub(super) mod password_reset; pub(super) mod password_reset;
pub(super) mod resources; pub(super) mod resources;
#[derive(Debug)]
pub(crate) struct TemplateContext {
pub allow_indexing: bool,
}
impl From<&crate::State> for TemplateContext {
fn from(state: &crate::State) -> Self {
Self {
allow_indexing: state.config.index_page_allow_indexing,
}
}
}
#[macro_export]
macro_rules! template {
(
struct $name:ident $(<$lifetime:lifetime>)? use $path:literal {
$($field_name:ident: $field_type:ty),*
}
) => {
#[derive(Debug, askama::Template)]
#[template(path = $path)]
struct $name$(<$lifetime>)? {
context: $crate::pages::TemplateContext,
$($field_name: $field_type,)*
}
impl$(<$lifetime>)? $name$(<$lifetime>)? {
fn new(state: &$crate::State, $($field_name: $field_type,)*) -> Self {
Self {
context: state.into(),
$($field_name,)*
}
}
}
#[allow(single_use_lifetimes)]
impl$(<$lifetime>)? axum::response::IntoResponse for $name$(<$lifetime>)? {
fn into_response(self) -> axum::response::Response {
match self.render() {
Ok(rendered) => axum::response::Html(rendered).into_response(),
Err(err) => $crate::WebError::from(err).into_response()
}
}
}
};
}

View file

@ -6,7 +6,7 @@ use axum::{
rejection::{FormRejection, QueryRejection}, rejection::{FormRejection, QueryRejection},
}, },
http::StatusCode, http::StatusCode,
response::{Html, IntoResponse, Response}, response::{IntoResponse, Response},
routing::get, routing::get,
}; };
use serde::Deserialize; use serde::Deserialize;
@ -15,6 +15,7 @@ use validator::Validate;
use crate::{ use crate::{
WebError, form, WebError, form,
pages::components::{UserCard, form::Form}, pages::components::{UserCard, form::Form},
template,
}; };
#[derive(Deserialize)] #[derive(Deserialize)]
@ -22,12 +23,11 @@ struct PasswordResetQuery {
token: String, token: String,
} }
#[derive(Debug, Template)] template! {
#[template(path = "password_reset.html.j2")] struct PasswordReset<'a> use "password_reset.html.j2" {
struct PasswordReset<'a> {
user_card: UserCard<'a>, user_card: UserCard<'a>,
body: PasswordResetBody, body: PasswordResetBody
allow_indexing: bool, }
} }
#[derive(Debug)] #[derive(Debug)]
@ -72,13 +72,8 @@ async fn password_reset_form(
let user_card = UserCard::for_local_user(&services, &token.info.user).await; let user_card = UserCard::for_local_user(&services, &token.info.user).await;
let template = PasswordReset { Ok(PasswordReset::new(&services, user_card, PasswordResetBody::Form(reset_form))
user_card, .into_response())
body: PasswordResetBody::Form(reset_form),
allow_indexing: services.config.index_page_allow_indexing,
};
Ok(Html(template.render()?))
} }
async fn get_password_reset( async fn get_password_reset(
@ -111,13 +106,8 @@ async fn post_password_reset(
.await?; .await?;
let user_card = UserCard::for_local_user(&services, &user_id).await; let user_card = UserCard::for_local_user(&services, &user_id).await;
let template = PasswordReset { Ok(PasswordReset::new(&services, user_card, PasswordResetBody::Success)
user_card, .into_response())
body: PasswordResetBody::Success,
allow_indexing: services.config.index_page_allow_indexing,
};
Ok(Html(template.render()?).into_response())
}, },
| Err(err) => Ok(( | Err(err) => Ok((
StatusCode::BAD_REQUEST, StatusCode::BAD_REQUEST,

View file

@ -5,7 +5,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>{% block title %}Continuwuity{% endblock %}</title> <title>{% block title %}Continuwuity{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
{%- if !allow_indexing %} {%- if !context.allow_indexing %}
<meta name="robots" content="noindex" /> <meta name="robots" content="noindex" />
{%- endif %} {%- endif %}