diff --git a/src/main.rs b/src/main.rs index 37e557c..d4b10f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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)] diff --git a/src/mycelium/mod.rs b/src/mycelium/mod.rs new file mode 100644 index 0000000..1241614 --- /dev/null +++ b/src/mycelium/mod.rs @@ -0,0 +1,3 @@ +mod view; + +pub use self::view::MycView; diff --git a/src/mycelium/view.rs b/src/mycelium/view.rs new file mode 100644 index 0000000..68b656a --- /dev/null +++ b/src/mycelium/view.rs @@ -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 { 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() + } +} diff --git a/src/templating_engine.rs b/src/templating_engine.rs index 4b97924..6bc1197 100644 --- a/src/templating_engine.rs +++ b/src/templating_engine.rs @@ -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 }, - 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 } } diff --git a/src/view.rs b/src/view.rs new file mode 100644 index 0000000..7bbfa9e --- /dev/null +++ b/src/view.rs @@ -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 }, + 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, + } + } +} +