echoip-slatecave/src/templating_engine.rs

136 lines
3.5 KiB
Rust

/*
* This is the echoip-slatecave templating engine.
* It wraps around tera in is specialized for echoip-slatecave.
*/
use axum::{
headers::HeaderValue,
http::StatusCode,
http::header::SET_COOKIE,
response::Html,
response::IntoResponse,
response::Response,
response::Json,
};
use axum_extra::extract::cookie::Cookie;
use axum_extra::extract::cookie;
use tera::Tera;
use toml::Table;
use crate::DigResult;
use crate::IpResult;
use crate::config::DnsResolverConfig;
use crate::settings::*;
/* The echoip view */
#[derive(serde::Serialize, Clone)]
#[serde(untagged)]
pub enum View {
Asn { asn: u32 },
Dig { query: String, result: DigResult },
DnsResolver{ config: DnsResolverConfig },
DnsResolverList,
Index { result: IpResult, user_agent: Option<String> },
Ip { result: IpResult },
Message{ title: String, message: String },
#[serde(rename="404")]
NotFound,
}
impl View {
pub fn template_name(&self) -> String {
match self {
View::Asn{..} => "asn",
View::Dig{..} => "dig",
View::DnsResolver{..} => "dns_resolver",
View::DnsResolverList => "dns_resolver_list",
View::Index{..} => "index",
View::Ip{..} => "ip",
View::Message{..} => "message",
View::NotFound => "404",
}.to_string()
}
}
/* The engine itself */
#[derive(Clone)]
pub struct Engine {
pub tera: Tera,
pub template_config: Option<Table>,
}
impl Engine {
pub async fn render_view(
&self,
settings: &QuerySettings,
view: &View,
) -> Response {
let mut response = match settings.format {
ResponseFormat::TextHtml | ResponseFormat::TextPlain => {
let template_name = view.template_name();
let mut context = tera::Context::new();
context.insert("view", &template_name);
//intented for shared macros
context.insert("format", &settings.format.to_string());
context.insert("language", &settings.lang);
context.insert("dns_resolvers", &settings.available_dns_resolvers);
context.insert("dns_resolver_id", &settings.dns_resolver_id);
context.insert("data", &view);
context.insert("extra", &self.template_config);
match self.tera.render(&(template_name+&settings.format.to_file_extension()), &context) {
Ok(text) =>
match settings.format {
ResponseFormat::TextHtml => Html(text).into_response(),
_ => text.into_response(),
}
Err(e) => {
println!("There was an error while rendering template {}: {e:?}", view.template_name());
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Template error in {}, contact owner or see logs.\n", view.template_name())
).into_response()
}
}
}
//TODO: Plain Text should have its own matcher
ResponseFormat::ApplicationJson => {
match view {
View::Dig{result, ..} => {
Json(result).into_response()
},
View::Index{result, ..} | View::Ip{result, ..} => {
Json(result).into_response()
},
View::DnsResolverList => {
Json(settings.available_dns_resolvers.clone()).into_response()
},
View::DnsResolver{ config } => {
Json(config).into_response()
}
_ => Json(view).into_response(),
}
}
};
match view {
View::NotFound => *response.status_mut() = StatusCode::NOT_FOUND,
_ => {},
}
let cookie = Cookie::build("dns_resolver",settings.dns_resolver_id.clone())
.path("/")
.same_site(cookie::SameSite::Strict)
.finish();
if let Ok(header_value) = HeaderValue::from_str(&cookie.to_string()) {
response.headers_mut().append(
SET_COOKIE,
header_value,
);
}
response
}
}