First step to detaching the templating from the logic.

This commit is contained in:
Slatian 2023-10-29 13:50:22 +01:00
parent c5a7597561
commit 20fb7ee2ff
5 changed files with 113 additions and 45 deletions

View File

@ -38,12 +38,14 @@ use std::sync::Arc;
mod config;
mod geoip;
mod idna;
mod ipinfo;
mod mycelium;
mod ratelimit;
mod settings;
mod simple_dns;
mod templating_engine;
mod idna;
mod view;
use crate::geoip::{
QueryAsn,
@ -54,7 +56,7 @@ use crate::geoip::{
use crate::idna::IdnaName;
use crate::simple_dns::DnsLookupResult;
use crate::settings::*;
use crate::templating_engine::View;
use crate::view::View;
use crate::ipinfo::{AddressCast,AddressInfo,AddressScope};
#[derive(Deserialize, Serialize, Clone)]

3
src/mycelium/mod.rs Normal file
View File

@ -0,0 +1,3 @@
mod view;
pub use self::view::MycView;

47
src/mycelium/view.rs Normal file
View File

@ -0,0 +1,47 @@
use axum::response::IntoResponse;
use axum::response::Json;
use axum::response::Response;
use axum::http::status::StatusCode;
use serde::Serialize;
pub trait MycView: Serialize + Sized {
/// Returns the template name that will be used to select
/// the template file.
///
/// If the name is "404" for an html response the template
/// file "404.html" will be used.
///
/// Also ends up as the `view` variable in the template.
fn get_template_name(&self) -> String;
//TODO: Add query settings to these
/// Returns the reponse code for the view.
///
/// The numeric value will be useable as `http_status` in the template.
fn get_status_code(&self) -> StatusCode;
/// If this returns a String it will be used as the cookie header.
///
/// See: [axum: Constructing a Cookie](https://docs.rs/axum-extra/0.8.0/axum_extra/extract/cookie/struct.Cookie.html#constructing-a-cookie)
fn get_cookie_header(&self) -> Option<String> { None }
/// Update non-API responses after they have been built.
///
/// Useful for setting extra headers. Does noting by default.
fn update_response(&self, _response: &mut Response) {}
/// Return an API-Response
///
/// By default causes the view to Serialize itself
/// to a json response using serde.
///
/// Response code and cookie headers are queried in
/// advance and set on the reulting response if it has a status code of 200.
/// Otherwise it is assumed that the response genrating logic
/// alredy took care of that.
fn get_api_response(self) -> Response {
Json(self).into_response()
}
}

View File

@ -17,42 +17,10 @@ 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()
}
}
use crate::view::View;
use crate::mycelium::MycView;
use crate::settings::QuerySettings;
use crate::settings::ResponseFormat;
/* The engine itself */
@ -70,7 +38,7 @@ impl Engine {
) -> Response {
let mut response = match settings.format {
ResponseFormat::TextHtml | ResponseFormat::TextPlain => {
let template_name = view.template_name();
let template_name = view.get_template_name();
let mut context = tera::Context::new();
context.insert("view", &template_name);
@ -82,17 +50,17 @@ impl Engine {
context.insert("data", &view);
context.insert("extra", &self.template_config);
match self.tera.render(&(template_name+&settings.format.to_file_extension()), &context) {
match self.tera.render(&(template_name.clone()+&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());
println!("There was an error while rendering template {}: {e:?}", template_name);
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Template error in {}, contact owner or see logs.\n", view.template_name())
format!("Template error in {}, contact owner or see logs.\n", template_name)
).into_response()
}
}
@ -116,10 +84,12 @@ impl Engine {
}
}
};
match view {
View::NotFound => *response.status_mut() = StatusCode::NOT_FOUND,
_ => {},
// Set status code
let status_code = view.get_status_code();
if status_code != StatusCode::OK {
*response.status_mut() = status_code;
}
// Set cookies
let cookie = Cookie::build("dns_resolver",settings.dns_resolver_id.to_string())
.path("/")
.same_site(cookie::SameSite::Strict)
@ -130,6 +100,7 @@ impl Engine {
header_value,
);
}
// return response
response
}
}

45
src/view.rs Normal file
View File

@ -0,0 +1,45 @@
use axum::http::status::StatusCode;
use crate::DigResult;
use crate::IpResult;
use crate::config::DnsResolverConfig;
use crate::mycelium::MycView;
#[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 MycView for View {
fn get_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()
}
fn get_status_code(&self) -> StatusCode {
match self {
Self::NotFound => StatusCode::NOT_FOUND,
_ => StatusCode::OK,
}
}
}