mirror of
https://codeberg.org/slatian/service.echoip-slatecave.git
synced 2024-12-26 11:48:21 +01:00
codename mycelium became lib-humus
This commit is contained in:
parent
912a119361
commit
f2e9e36e99
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -492,6 +492,7 @@ dependencies = [
|
|||||||
"governor",
|
"governor",
|
||||||
"idna 0.3.0",
|
"idna 0.3.0",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"lib-humus",
|
||||||
"maxminddb",
|
"maxminddb",
|
||||||
"mime",
|
"mime",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
@ -1011,6 +1012,19 @@ version = "1.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lib-humus"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://codeberg.org/slatian/lib-humus.git#5c9315b314ba0c247d236d6318041c6872c90b04"
|
||||||
|
dependencies = [
|
||||||
|
"axum",
|
||||||
|
"axum-extra",
|
||||||
|
"mime",
|
||||||
|
"serde",
|
||||||
|
"tera",
|
||||||
|
"toml",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.149"
|
version = "0.2.149"
|
||||||
|
@ -7,6 +7,8 @@ authors = ["Slatian <baschdel@disroot.org>"]
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
lib-humus = { version="0.1", features=["axum-view+cookie"], git="https://codeberg.org/slatian/lib-humus.git" }
|
||||||
|
|
||||||
axum = { version = "0.6", features = ["macros", "headers"] }
|
axum = { version = "0.6", features = ["macros", "headers"] }
|
||||||
axum-extra = { version = "0.7", features = ["cookie"] }
|
axum-extra = { version = "0.7", features = ["cookie"] }
|
||||||
axum-client-ip = "0.4"
|
axum-client-ip = "0.4"
|
||||||
|
10
src/main.rs
10
src/main.rs
@ -34,11 +34,14 @@ use std::collections::HashMap;
|
|||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use lib_humus::TemplateEngineLoader;
|
||||||
|
use lib_humus::read_toml_from_file;
|
||||||
|
use lib_humus::HumusEngine;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod geoip;
|
mod geoip;
|
||||||
mod idna;
|
mod idna;
|
||||||
mod ipinfo;
|
mod ipinfo;
|
||||||
mod mycelium;
|
|
||||||
mod ratelimit;
|
mod ratelimit;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod simple_dns;
|
mod simple_dns;
|
||||||
@ -51,15 +54,12 @@ use crate::geoip::{
|
|||||||
LocationResult,
|
LocationResult,
|
||||||
};
|
};
|
||||||
use crate::idna::IdnaName;
|
use crate::idna::IdnaName;
|
||||||
use crate::mycelium::MycEngine;
|
|
||||||
use crate::mycelium::TemplateEngineLoader;
|
|
||||||
use crate::mycelium::read_toml_from_file;
|
|
||||||
use crate::simple_dns::DnsLookupResult;
|
use crate::simple_dns::DnsLookupResult;
|
||||||
use crate::settings::*;
|
use crate::settings::*;
|
||||||
use crate::view::View;
|
use crate::view::View;
|
||||||
use crate::ipinfo::{AddressCast,AddressInfo,AddressScope};
|
use crate::ipinfo::{AddressCast,AddressInfo,AddressScope};
|
||||||
|
|
||||||
type TemplatingEngine = MycEngine<View,QuerySettings,ResponseFormat>;
|
type TemplatingEngine = HumusEngine<View,QuerySettings,ResponseFormat>;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone)]
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
pub struct SettingsQuery {
|
pub struct SettingsQuery {
|
||||||
|
@ -1,146 +0,0 @@
|
|||||||
|
|
||||||
use axum::{
|
|
||||||
body::{Bytes,Full},
|
|
||||||
headers::HeaderValue,
|
|
||||||
http::StatusCode,
|
|
||||||
http::header,
|
|
||||||
http::header::SET_COOKIE,
|
|
||||||
response::IntoResponse,
|
|
||||||
response::Response,
|
|
||||||
};
|
|
||||||
use tera::Tera;
|
|
||||||
use toml::Table;
|
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use crate::mycelium::MycView;
|
|
||||||
use crate::mycelium::MycFormat;
|
|
||||||
use crate::mycelium::MycFormatFamily;
|
|
||||||
use crate::mycelium::MycQuerySettings;
|
|
||||||
|
|
||||||
/* The engine itself */
|
|
||||||
|
|
||||||
/// A constructable version of MycEngine without the types attached.
|
|
||||||
///
|
|
||||||
/// Can be converted to a MycEngine using `into()`.
|
|
||||||
pub struct MycProtoEngine {
|
|
||||||
pub tera: Tera,
|
|
||||||
pub template_config: Option<Table>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct MycEngine<V, S, F>
|
|
||||||
where V: MycView<S, F>, S: MycQuerySettings<F>, F: MycFormat {
|
|
||||||
pub tera: Tera,
|
|
||||||
pub template_config: Option<Table>,
|
|
||||||
|
|
||||||
phantom_view: PhantomData<V>,
|
|
||||||
phantom_settings: PhantomData<S>,
|
|
||||||
phantom_format: PhantomData<F>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V, S, F> MycEngine<V, S, F>
|
|
||||||
where V: MycView<S, F>, S: MycQuerySettings<F>, F: MycFormat {
|
|
||||||
|
|
||||||
pub fn new(tera: Tera, template_config: Option<Table>) -> Self {
|
|
||||||
Self {
|
|
||||||
tera: tera,
|
|
||||||
template_config: template_config,
|
|
||||||
phantom_view: PhantomData,
|
|
||||||
phantom_settings: PhantomData,
|
|
||||||
phantom_format: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Takes settings and a view, converting it to a serveable response.
|
|
||||||
pub async fn render_view(
|
|
||||||
&self,
|
|
||||||
settings: &S,
|
|
||||||
view: V,
|
|
||||||
) -> Response {
|
|
||||||
let status_code = view.get_status_code(&settings);
|
|
||||||
let cookie_string = view.get_cookie_header(&settings);
|
|
||||||
let format = settings.get_format();
|
|
||||||
|
|
||||||
let mut response = match format.get_family() {
|
|
||||||
MycFormatFamily::Template => {
|
|
||||||
let template_name = view.get_template_name();
|
|
||||||
let mime_type = format.get_mime_type();
|
|
||||||
|
|
||||||
let mut context = tera::Context::new();
|
|
||||||
context.insert("view", &template_name);
|
|
||||||
//intented for shared macros
|
|
||||||
context.insert("format", &format.get_name());
|
|
||||||
context.insert("mimetype", &mime_type.to_string());
|
|
||||||
context.insert("data", &view);
|
|
||||||
context.insert("extra", &self.template_config);
|
|
||||||
settings.initalize_template_context(&mut context);
|
|
||||||
|
|
||||||
match self.tera.render(&(template_name.clone()+&format.get_file_extension()), &context) {
|
|
||||||
Ok(text) => {
|
|
||||||
let mut response = (
|
|
||||||
[(
|
|
||||||
header::CONTENT_TYPE,
|
|
||||||
HeaderValue::from_str(mime_type.as_ref())
|
|
||||||
.expect("MimeType should always be a valid header value.")
|
|
||||||
)],
|
|
||||||
Into::<Full<Bytes>>::into(text),
|
|
||||||
).into_response();
|
|
||||||
view.update_response(&mut response, &settings);
|
|
||||||
response
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
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", template_name)
|
|
||||||
).into_response()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MycFormatFamily::API => {
|
|
||||||
view.get_api_response(&settings)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Everything went well and nobody did the following work for us.
|
|
||||||
if response.status() == StatusCode::OK {
|
|
||||||
|
|
||||||
// Set status code
|
|
||||||
if status_code != StatusCode::OK {
|
|
||||||
*response.status_mut() = status_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set cookie header
|
|
||||||
if let Some(cookie_string) = cookie_string {
|
|
||||||
if let Ok(header_value) = HeaderValue::from_str(&cookie_string) {
|
|
||||||
response.headers_mut().append(
|
|
||||||
SET_COOKIE,
|
|
||||||
header_value,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// return response
|
|
||||||
response
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V, S, F> From<MycProtoEngine> for MycEngine<V, S, F>
|
|
||||||
where V: MycView<S, F>, S: MycQuerySettings<F>, F: MycFormat {
|
|
||||||
fn from(e: MycProtoEngine) -> Self {
|
|
||||||
Self::new(e.tera, e.template_config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V, S, F> From<MycEngine<V, S, F>> for MycProtoEngine
|
|
||||||
where V: MycView<S, F>, S: MycQuerySettings<F>, F: MycFormat {
|
|
||||||
fn from(e: MycEngine<V, S, F>) -> Self {
|
|
||||||
Self {
|
|
||||||
tera: e.tera,
|
|
||||||
template_config: e.template_config,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,132 +0,0 @@
|
|||||||
|
|
||||||
use serde::Deserialize;
|
|
||||||
use serde::Serialize;
|
|
||||||
use mime::Mime;
|
|
||||||
|
|
||||||
/// Defines how the response should be rendered.
|
|
||||||
pub enum MycFormatFamily {
|
|
||||||
/// When rendering the templating engine will be invoked
|
|
||||||
Template,
|
|
||||||
/// When rendering the [View](./trait.View.html)
|
|
||||||
/// is asked to generate an API response.
|
|
||||||
API,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implement on a type that is able to describe a response format.
|
|
||||||
///
|
|
||||||
/// It is best implemented on an enum.
|
|
||||||
pub trait MycFormat: ToString+Clone+Default+Send {
|
|
||||||
|
|
||||||
// Return the format family this
|
|
||||||
fn get_family(&self) -> MycFormatFamily {
|
|
||||||
match self.get_name().as_str() {
|
|
||||||
"json" => MycFormatFamily::API,
|
|
||||||
_ => MycFormatFamily::Template,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the file extnsion for the format.
|
|
||||||
///
|
|
||||||
/// Used for deriving the path for the template name.
|
|
||||||
///
|
|
||||||
/// Defaults to `.{self.get_name()}`
|
|
||||||
/// with the exception of the name being `text`
|
|
||||||
/// then it defaults to `.txt`.
|
|
||||||
fn get_file_extension(&self) -> String {
|
|
||||||
match self.get_name().as_str() {
|
|
||||||
"text" => ".txt".to_string(),
|
|
||||||
_ => ".".to_owned()+&self.get_name(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the name of the format,
|
|
||||||
/// by default taken from the ToString implementation.
|
|
||||||
fn get_name(&self) -> String {
|
|
||||||
self.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allows adding extra mimetypes quickly for prototyping
|
|
||||||
///
|
|
||||||
/// Implementing get_mime_type() properly is recommended
|
|
||||||
/// for production use.
|
|
||||||
fn get_less_well_known_mimetype(&self) -> Option<Mime> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a textual representation of the Mimetype.
|
|
||||||
///
|
|
||||||
/// It is recommended to implement this when in production use.
|
|
||||||
///
|
|
||||||
/// For prototyping the default implementation makes assumptions
|
|
||||||
/// based on the output of get_name(), falling back
|
|
||||||
/// to get_less_well_known_mimetype() and the "application/octet-stream" type.
|
|
||||||
///
|
|
||||||
/// The default implementation knows the following associations:
|
|
||||||
/// * `text`: `text/plain; charset=utf-8`
|
|
||||||
/// * `html`: `text/html; charset=utf-8`
|
|
||||||
/// * `json`: `application/json`
|
|
||||||
/// * `xml`: `application/xml`
|
|
||||||
/// * `rss`: `application/rss+xml`
|
|
||||||
/// * `atom`: `application/atom+xml`
|
|
||||||
///
|
|
||||||
/// *Implementation Note:* It may be possible that two different views
|
|
||||||
/// have the same MimeType (maybe two json representations for different consumers).
|
|
||||||
///
|
|
||||||
fn get_mime_type(&self) -> Mime {
|
|
||||||
match self.get_name().as_str() {
|
|
||||||
"text" => mime::TEXT_PLAIN_UTF_8,
|
|
||||||
"html" => mime::TEXT_HTML_UTF_8,
|
|
||||||
"json" => mime::APPLICATION_JSON,
|
|
||||||
"xml" => "application/xml".parse()
|
|
||||||
.expect("Parse static application/xml"),
|
|
||||||
"rss" => "application/rss+xml".parse()
|
|
||||||
.expect("Parse static application/rss+xml"),
|
|
||||||
"atom" => "application/atom+xml".parse()
|
|
||||||
.expect("Parse static application/atom+xml"),
|
|
||||||
_ =>
|
|
||||||
self.get_less_well_known_mimetype()
|
|
||||||
.unwrap_or(mime::APPLICATION_OCTET_STREAM),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a view from its name.
|
|
||||||
fn from_name(name: &str) -> Option<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some Sample implementations
|
|
||||||
|
|
||||||
#[derive(Clone,Serialize,Deserialize,Default)]
|
|
||||||
#[serde(rename_all="lowercase")]
|
|
||||||
pub enum HtmlTextJsonFormat {
|
|
||||||
#[default]
|
|
||||||
Html,
|
|
||||||
Text,
|
|
||||||
Json,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToString for HtmlTextJsonFormat {
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Self::Html => "html",
|
|
||||||
Self::Text => "text",
|
|
||||||
Self::Json => "json",
|
|
||||||
}.to_owned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl MycFormat for HtmlTextJsonFormat {
|
|
||||||
|
|
||||||
//TODO: implement other methods to make it more performant
|
|
||||||
|
|
||||||
fn from_name(name: &str) -> Option<Self> {
|
|
||||||
match name {
|
|
||||||
"html" => Some(Self::Html),
|
|
||||||
"text" => Some(Self::Text),
|
|
||||||
"json" => Some(Self::Json),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
|||||||
mod engine;
|
|
||||||
mod format;
|
|
||||||
mod query_settings;
|
|
||||||
mod template_loader;
|
|
||||||
mod toml_helper;
|
|
||||||
mod view;
|
|
||||||
|
|
||||||
pub use self::engine::MycEngine;
|
|
||||||
pub use self::engine::MycProtoEngine;
|
|
||||||
pub use self::format::HtmlTextJsonFormat;
|
|
||||||
pub use self::format::MycFormat;
|
|
||||||
pub use self::format::MycFormatFamily;
|
|
||||||
pub use self::query_settings::MycQuerySettings;
|
|
||||||
pub use self::template_loader::TemplateEngineLoader;
|
|
||||||
pub use self::toml_helper::read_toml_from_file;
|
|
||||||
pub use self::toml_helper::TomlError;
|
|
||||||
pub use self::view::MycView;
|
|
@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
use tera::Context;
|
|
||||||
|
|
||||||
use crate::mycelium::MycFormat;
|
|
||||||
|
|
||||||
pub trait MycQuerySettings<F>
|
|
||||||
where F: MycFormat {
|
|
||||||
|
|
||||||
/// Called before rendering a template to initalize it with
|
|
||||||
/// values that come from the query itself.
|
|
||||||
fn initalize_template_context(&self, context: &mut Context);
|
|
||||||
|
|
||||||
/// Returns the requested output format
|
|
||||||
fn get_format(&self) -> F;
|
|
||||||
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
|
|
||||||
use tera::Tera;
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use crate::mycelium::MycProtoEngine;
|
|
||||||
use crate::mycelium::read_toml_from_file;
|
|
||||||
use crate::mycelium::TomlError;
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone)]
|
|
||||||
pub struct TemplateEngineLoader {
|
|
||||||
pub template_location: String,
|
|
||||||
pub extra_config_location: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TemplateEngineLoader {
|
|
||||||
|
|
||||||
pub fn new(
|
|
||||||
template_location: String,
|
|
||||||
extra_config_location: Option<String>
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
template_location: template_location,
|
|
||||||
extra_config_location: extra_config_location,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cli_template_location(mut self, location: Option<String>) -> Self {
|
|
||||||
if let Some(location) = location {
|
|
||||||
self.template_location = location;
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cli_extra_config_location(mut self, location: Option<String>) -> Self {
|
|
||||||
if let Some(location) = location {
|
|
||||||
self.extra_config_location = Some(location);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the template base directory.
|
|
||||||
//
|
|
||||||
// Contructed by ensuring the template_location ends in a "/".
|
|
||||||
pub fn base_dir(&self) -> String {
|
|
||||||
if self.template_location.ends_with("/") {
|
|
||||||
self.template_location.clone()
|
|
||||||
} else {
|
|
||||||
self.template_location.clone()+"/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_templates(
|
|
||||||
&self
|
|
||||||
) -> Result<MycProtoEngine,TemplateEngineLoaderError> {
|
|
||||||
let template_base_dir = self.base_dir();
|
|
||||||
let template_extra_config_res = match &self.extra_config_location {
|
|
||||||
Some(path) => read_toml_from_file(path),
|
|
||||||
None => {
|
|
||||||
read_toml_from_file(&(template_base_dir.clone()+"extra.toml"))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let template_extra_config = match template_extra_config_res {
|
|
||||||
Ok(c) => Some(c),
|
|
||||||
Err(e) => match &e {
|
|
||||||
TomlError::FileError{..} => {
|
|
||||||
// Only fatal if the file was explicitly requested.
|
|
||||||
// An implicit request could also mean that
|
|
||||||
// the template doesn't need a config file.
|
|
||||||
if self.extra_config_location.is_some() {
|
|
||||||
return Err(TemplateEngineLoaderError::TomlError(e));
|
|
||||||
}
|
|
||||||
None
|
|
||||||
},
|
|
||||||
TomlError::ParseError{..} => {
|
|
||||||
return Err(TemplateEngineLoaderError::TomlError(e));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let template_glob = template_base_dir.clone()+"*";
|
|
||||||
println!("Parsing Templates from '{}' ...", &template_glob);
|
|
||||||
let res = Tera::new((template_glob).as_str());
|
|
||||||
let tera = match res {
|
|
||||||
Ok(t) => t,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(TemplateEngineLoaderError::TemplateParseError{
|
|
||||||
path: template_glob,
|
|
||||||
tera_error: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(MycProtoEngine {
|
|
||||||
tera: tera,
|
|
||||||
template_config: template_extra_config,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum TemplateEngineLoaderError {
|
|
||||||
TomlError(TomlError),
|
|
||||||
TemplateParseError{path: String, tera_error: tera::Error },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for TemplateEngineLoaderError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::TomlError(e) =>
|
|
||||||
write!(f,"Error with template extra configuration:\n(Depending on you template the file not being found might be okay)\n{e}"),
|
|
||||||
Self::TemplateParseError{path, tera_error} =>
|
|
||||||
write!(f,"Error parsing template '{path}':\n{tera_error}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::fs;
|
|
||||||
|
|
||||||
pub fn read_toml_from_file<T>(path: &String) -> Result<T,TomlError>
|
|
||||||
where T: for<'de> Deserialize<'de> {
|
|
||||||
let text = match fs::read_to_string(path) {
|
|
||||||
Ok(t) => t,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(TomlError::FileError{
|
|
||||||
path: path.clone(),
|
|
||||||
io_error: e
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match toml::from_str(&text) {
|
|
||||||
Ok(t) => Ok(t),
|
|
||||||
Err(e) => {
|
|
||||||
return Err(TomlError::ParseError{
|
|
||||||
path: path.clone(),
|
|
||||||
toml_error: e
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum TomlError {
|
|
||||||
FileError{ path: String, io_error: std::io::Error },
|
|
||||||
ParseError{ path: String, toml_error: toml::de::Error },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for TomlError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::FileError{path, io_error} =>
|
|
||||||
write!(f,"Error while reading file '{path}': {io_error}"),
|
|
||||||
Self::ParseError{path, toml_error} =>
|
|
||||||
write!(f,"Unable to parse file as toml '{path}':\n{toml_error}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
use axum::response::IntoResponse;
|
|
||||||
use axum::response::Json;
|
|
||||||
use axum::response::Response;
|
|
||||||
use axum::http::status::StatusCode;
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
use crate::mycelium::MycQuerySettings;
|
|
||||||
use crate::mycelium::MycFormat;
|
|
||||||
|
|
||||||
pub trait MycView<S,F>: Serialize + Sized
|
|
||||||
where S: MycQuerySettings<F>, F: MycFormat {
|
|
||||||
|
|
||||||
/// 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, settings: &S) -> 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, _settings: &S) -> 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, _settings: &S) {}
|
|
||||||
|
|
||||||
/// 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, _settings: &S) -> Response {
|
|
||||||
Json(self).into_response()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,9 @@
|
|||||||
use serde::{Deserialize,Serialize};
|
use serde::{Deserialize,Serialize};
|
||||||
|
|
||||||
|
use lib_humus::HtmlTextJsonFormat;
|
||||||
|
use lib_humus::HumusQuerySettings;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use crate::mycelium::HtmlTextJsonFormat;
|
|
||||||
use crate::mycelium::MycQuerySettings;
|
|
||||||
|
|
||||||
/* Response format */
|
/* Response format */
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ pub struct Selectable {
|
|||||||
pub weight: i32,
|
pub weight: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MycQuerySettings<ResponseFormat> for QuerySettings {
|
impl HumusQuerySettings<ResponseFormat> for QuerySettings {
|
||||||
|
|
||||||
fn initalize_template_context(&self, context: &mut tera::Context) {
|
fn initalize_template_context(&self, context: &mut tera::Context) {
|
||||||
context.insert("language", &self.lang);
|
context.insert("language", &self.lang);
|
||||||
|
@ -5,6 +5,7 @@ use axum::response::IntoResponse;
|
|||||||
use axum::response::Response;
|
use axum::response::Response;
|
||||||
use axum_extra::extract::cookie::Cookie;
|
use axum_extra::extract::cookie::Cookie;
|
||||||
use axum_extra::extract::cookie;
|
use axum_extra::extract::cookie;
|
||||||
|
use lib_humus::HumusView;
|
||||||
|
|
||||||
use crate::DigResult;
|
use crate::DigResult;
|
||||||
use crate::IpResult;
|
use crate::IpResult;
|
||||||
@ -12,7 +13,6 @@ use crate::config::DnsResolverConfig;
|
|||||||
use crate::settings::QuerySettings;
|
use crate::settings::QuerySettings;
|
||||||
use crate::settings::ResponseFormat;
|
use crate::settings::ResponseFormat;
|
||||||
|
|
||||||
use crate::mycelium::MycView;
|
|
||||||
|
|
||||||
#[derive(serde::Serialize, Clone)]
|
#[derive(serde::Serialize, Clone)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
@ -28,7 +28,7 @@ pub enum View {
|
|||||||
NotFound,
|
NotFound,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MycView<QuerySettings, ResponseFormat> for View {
|
impl HumusView<QuerySettings, ResponseFormat> for View {
|
||||||
fn get_template_name(&self) -> String {
|
fn get_template_name(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
View::Asn{..} => "asn",
|
View::Asn{..} => "asn",
|
||||||
|
Loading…
Reference in New Issue
Block a user