mirror of
https://codeberg.org/slatian/service.echoip-slatecave.git
synced 2025-01-14 12:37:08 +01:00
Middlewarre!
This commit is contained in:
parent
2abc5844ad
commit
52ace5f61f
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -45,6 +45,7 @@ checksum = "678c5130a507ae3a7c797f9a17393c14849300b8440eac47cdb90a5bdcb3a543"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"axum-macros",
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
@ -98,6 +99,18 @@ dependencies = [
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-macros"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fbf955307ff8addb48d2399393c9e2740dd491537ec562b66ab364fc4a38841"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@ -1150,6 +1163,7 @@ dependencies = [
|
||||
"tera",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tower",
|
||||
"trust-dns-resolver",
|
||||
]
|
||||
|
||||
|
@ -7,12 +7,13 @@ authors = ["Slatian <baschdel@disroot.org>"]
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
axum = "0.6"
|
||||
axum = { version = "0.6", features = ["macros"] }
|
||||
axum-client-ip = "0.4"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tera = "1"
|
||||
toml = "0.7"
|
||||
tower = "*"
|
||||
trust-dns-resolver = "0.22"
|
||||
maxminddb = "0.17"
|
||||
|
@ -1,7 +1,7 @@
|
||||
use axum_client_ip::SecureClientIpSource;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[derive(serde::Deserialize, Default)]
|
||||
#[derive(serde::Deserialize, Default, Clone)]
|
||||
pub struct EchoIpServiceConfig {
|
||||
pub server: ServerConfig,
|
||||
pub dns: DnsConfig,
|
||||
@ -9,7 +9,7 @@ pub struct EchoIpServiceConfig {
|
||||
pub template: TemplateConfig,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[derive(serde::Deserialize, Clone)]
|
||||
pub struct ServerConfig {
|
||||
pub listen_on: SocketAddr,
|
||||
pub ip_header: SecureClientIpSource,
|
||||
@ -17,7 +17,7 @@ pub struct ServerConfig {
|
||||
pub allow_private_ip_lookup: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[derive(serde::Deserialize, Clone)]
|
||||
pub struct DnsConfig {
|
||||
pub allow_forward_lookup: bool,
|
||||
pub allow_reverse_lookup: bool,
|
||||
@ -25,13 +25,13 @@ pub struct DnsConfig {
|
||||
//Future Idea: allow custom resolver
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[derive(serde::Deserialize, Clone)]
|
||||
pub struct GeoIpConfig {
|
||||
pub asn_database: Option<String>,
|
||||
pub location_database: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[derive(serde::Deserialize, Clone)]
|
||||
pub struct TemplateConfig {
|
||||
pub template_location: String,
|
||||
pub extra_config: Option<String>,
|
||||
|
@ -12,20 +12,20 @@ use std::path::Path;
|
||||
|
||||
/* Datatypes */
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize, Default)]
|
||||
#[derive(serde::Deserialize, serde::Serialize, Default, Clone)]
|
||||
pub struct NamedLocation {
|
||||
iso_code: Option<String>,
|
||||
name: Option<String>,
|
||||
geoname_id: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize, Default)]
|
||||
#[derive(serde::Deserialize, serde::Serialize, Default, Copy, Clone)]
|
||||
pub struct LocationCoordinates {
|
||||
latitude: f64,
|
||||
logtitude: f64,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize, Default)]
|
||||
#[derive(serde::Deserialize, serde::Serialize, Default, Clone)]
|
||||
pub struct LocationResult {
|
||||
continent: Option<NamedLocation>,
|
||||
country: Option<NamedLocation>,
|
||||
@ -38,7 +38,7 @@ pub struct LocationResult {
|
||||
time_zone: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize, Default)]
|
||||
#[derive(serde::Deserialize, serde::Serialize, Default, Clone)]
|
||||
pub struct AsnResult {
|
||||
asn: Option<u32>,
|
||||
name: Option<String>,
|
||||
|
@ -8,7 +8,7 @@
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Default, PartialEq, Clone)]
|
||||
#[serde(rename_all="lowercase")]
|
||||
pub enum AddressCast {
|
||||
Unspecified,
|
||||
@ -18,7 +18,7 @@ pub enum AddressCast {
|
||||
Broadcast,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Default, PartialEq, Clone)]
|
||||
#[serde(rename_all="lowercase")]
|
||||
pub enum AddressScope {
|
||||
Global,
|
||||
@ -32,7 +32,7 @@ pub enum AddressScope {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
#[derive(Serialize, Deserialize, Default, Clone)]
|
||||
pub struct AddressInfo {
|
||||
pub is_v6_address: bool,
|
||||
pub cast: AddressCast,
|
||||
|
113
src/main.rs
113
src/main.rs
@ -1,7 +1,12 @@
|
||||
use axum::{
|
||||
extract::Query,
|
||||
extract::State,
|
||||
extract,
|
||||
extract::{
|
||||
self,
|
||||
Query,
|
||||
State,
|
||||
Extension,
|
||||
},
|
||||
http::Request,
|
||||
middleware::{self, Next},
|
||||
response::Response,
|
||||
Router,
|
||||
routing::get,
|
||||
@ -9,6 +14,7 @@ use axum::{
|
||||
use axum_client_ip::SecureClientIp;
|
||||
use clap::Parser;
|
||||
use tera::Tera;
|
||||
use tower::ServiceBuilder;
|
||||
use trust_dns_resolver::{
|
||||
TokioAsyncResolver,
|
||||
// config::ResolverOpts,
|
||||
@ -36,26 +42,29 @@ use crate::templating_engine::ResponseFormat;
|
||||
|
||||
use crate::ipinfo::{AddressCast,AddressInfo,AddressScope};
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[derive(serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct BaseQuery {
|
||||
format: Option<ResponseFormat>,
|
||||
lang: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[derive(serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct QuerySettings {
|
||||
format: ResponseFormat,
|
||||
lang: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct IpQuery {
|
||||
format: Option<ResponseFormat>,
|
||||
lang: Option<String>,
|
||||
ip: IpAddr,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[derive(serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct DigQuery {
|
||||
format: Option<ResponseFormat>,
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[derive(serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct IpResult {
|
||||
hostname: Option<String>,
|
||||
asn: Option<AsnResult>,
|
||||
@ -158,6 +167,11 @@ async fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
let templating_engine = templating_engine::Engine{
|
||||
tera: tera,
|
||||
template_config: template_extra_config,
|
||||
};
|
||||
|
||||
// Initalize GeoIP Database
|
||||
|
||||
let mut asn_db = geoip::MMDBCarrier {
|
||||
@ -198,14 +212,11 @@ async fn main() {
|
||||
// Initialize shared state
|
||||
let shared_state = Arc::new(
|
||||
ServiceSharedState {
|
||||
templating_engine: templating_engine::Engine{
|
||||
tera: tera,
|
||||
template_config: template_extra_config,
|
||||
},
|
||||
templating_engine: templating_engine,
|
||||
dns_resolver: dns_resolver,
|
||||
asn_db: asn_db,
|
||||
location_db: location_db,
|
||||
config: config,
|
||||
config: config.clone(),
|
||||
});
|
||||
|
||||
// Initalize axum server
|
||||
@ -217,7 +228,12 @@ async fn main() {
|
||||
.route("/ip/:address", get(handle_ip_route_with_path))
|
||||
.route("/hi", get(hello_world_handler))
|
||||
.with_state(shared_state)
|
||||
.layer(
|
||||
ServiceBuilder::new()
|
||||
.layer(ip_header.into_extension())
|
||||
.layer(Extension(config))
|
||||
.layer(middleware::from_fn(format_and_language_middleware))
|
||||
)
|
||||
;
|
||||
|
||||
println!("Starting Server ...");
|
||||
@ -228,77 +244,90 @@ async fn main() {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn format_and_language_middleware<B>(
|
||||
Query(query): Query<BaseQuery>,
|
||||
Extension(config): Extension<config::EchoIpServiceConfig>,
|
||||
mut req: Request<B>,
|
||||
next: Next<B>
|
||||
) -> Response {
|
||||
let format = query.format.unwrap_or(ResponseFormat::TextHtml);
|
||||
req.extensions_mut().insert(QuerySettings{
|
||||
format: format,
|
||||
lang: query.lang.unwrap_or("en".to_string()),
|
||||
});
|
||||
next.run(req).await
|
||||
}
|
||||
|
||||
#[axum::debug_handler]
|
||||
async fn hello_world_handler(
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
Extension(settings): Extension<QuerySettings>,
|
||||
) -> Response {
|
||||
let state = Arc::clone(&arc_state);
|
||||
|
||||
|
||||
state.templating_engine.render_view(
|
||||
ResponseFormat::TextPlain,
|
||||
View::Message("Hello! There, You, Awesome Creature!".to_string())
|
||||
settings.format,
|
||||
&View::Message("Hello! There, You, Awesome Creature!".to_string())
|
||||
).await
|
||||
}
|
||||
|
||||
async fn handle_default_route(
|
||||
Query(query): Query<BaseQuery>,
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
Extension(settings): Extension<QuerySettings>,
|
||||
SecureClientIp(address): SecureClientIp
|
||||
) -> Response {
|
||||
|
||||
let format = query.format.unwrap_or(ResponseFormat::TextHtml);
|
||||
|
||||
let ip_query = IpQuery {
|
||||
format: query.format,
|
||||
lang: query.lang,
|
||||
ip: address,
|
||||
};
|
||||
|
||||
let state = Arc::clone(&arc_state);
|
||||
|
||||
let result = get_ip_result(&ip_query, &state).await;
|
||||
let result = get_ip_result(&ip_query, &settings.lang, &state).await;
|
||||
|
||||
state.templating_engine.render_view(
|
||||
format,
|
||||
View::Index{query: ip_query, result: result}
|
||||
settings.format,
|
||||
&View::Index{query: ip_query, result: result}
|
||||
).await
|
||||
}
|
||||
|
||||
async fn handle_ip_route(
|
||||
Query(ip_query): Query<IpQuery>,
|
||||
Extension(settings): Extension<QuerySettings>,
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
) -> Response {
|
||||
return handle_ip_request(ip_query, arc_state).await
|
||||
return handle_ip_request(ip_query, settings, arc_state).await
|
||||
}
|
||||
|
||||
async fn handle_ip_route_with_path(
|
||||
Query(query): Query<BaseQuery>,
|
||||
Extension(settings): Extension<QuerySettings>,
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
extract::Path(address): extract::Path<IpAddr>,
|
||||
) -> Response {
|
||||
return handle_ip_request(IpQuery {
|
||||
format: query.format,
|
||||
lang: query.lang,
|
||||
ip: address,
|
||||
}, arc_state).await
|
||||
}, settings, arc_state).await
|
||||
}
|
||||
|
||||
async fn handle_ip_request(
|
||||
ip_query: IpQuery,
|
||||
settings: QuerySettings,
|
||||
arc_state: Arc<ServiceSharedState>,
|
||||
) -> Response {
|
||||
|
||||
let state = Arc::clone(&arc_state);
|
||||
let result = get_ip_result(&ip_query, &state).await;
|
||||
let format = ip_query.format.unwrap_or(ResponseFormat::TextHtml);
|
||||
let result = get_ip_result(&ip_query, &settings.lang, &state).await;
|
||||
|
||||
state.templating_engine.render_view(
|
||||
format,
|
||||
View::Ip{query: ip_query, result: result}
|
||||
settings.format,
|
||||
&View::Ip{query: ip_query, result: result}
|
||||
).await
|
||||
}
|
||||
|
||||
async fn get_ip_result(
|
||||
ip_query: &IpQuery,
|
||||
lang: &String,
|
||||
state: &ServiceSharedState,
|
||||
) -> IpResult {
|
||||
let address = ip_query.ip;
|
||||
@ -329,7 +358,7 @@ async fn get_ip_result(
|
||||
// location lookup
|
||||
let location_result = state.location_db.query_location_for_ip(
|
||||
address,
|
||||
&vec![&ip_query.lang.as_ref().unwrap_or(&"en".to_string()), &"en".to_string()]
|
||||
&vec![lang, &"en".to_string()]
|
||||
);
|
||||
|
||||
// filter reverse lookup
|
||||
@ -354,35 +383,35 @@ async fn get_ip_result(
|
||||
|
||||
async fn handle_dig_route(
|
||||
Query(dig_query): Query<DigQuery>,
|
||||
Extension(settings): Extension<QuerySettings>,
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
) -> Response {
|
||||
return handle_dig_request(dig_query, arc_state).await
|
||||
return handle_dig_request(dig_query, settings, arc_state).await
|
||||
}
|
||||
|
||||
async fn handle_dig_route_with_path(
|
||||
Query(query): Query<BaseQuery>,
|
||||
Extension(settings): Extension<QuerySettings>,
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
extract::Path(name): extract::Path<String>,
|
||||
) -> Response {
|
||||
return handle_dig_request(DigQuery {
|
||||
format: query.format,
|
||||
name: name,
|
||||
}, arc_state).await
|
||||
}, settings, arc_state).await
|
||||
}
|
||||
|
||||
async fn handle_dig_request(
|
||||
dig_query: DigQuery,
|
||||
settings: QuerySettings,
|
||||
arc_state: Arc<ServiceSharedState>,
|
||||
) -> Response {
|
||||
|
||||
let state = Arc::clone(&arc_state);
|
||||
let format = dig_query.format.unwrap_or(ResponseFormat::TextHtml);
|
||||
|
||||
let dig_result = get_dig_result(&dig_query, &state).await;
|
||||
|
||||
state.templating_engine.render_view(
|
||||
format,
|
||||
View::Dig{ query: dig_query, result: dig_result}
|
||||
settings.format,
|
||||
&View::Dig{ query: dig_query, result: dig_result}
|
||||
).await
|
||||
|
||||
}
|
||||
|
@ -16,15 +16,14 @@ use std::net::IpAddr;
|
||||
|
||||
/* Data Structures */
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Default)]
|
||||
#[derive(serde::Deserialize, serde::Serialize, Default, Clone)]
|
||||
pub struct DnsLookupResult {
|
||||
a: Vec<IpAddr>,
|
||||
aaaa: Vec<IpAddr>,
|
||||
mx: Vec<MxRecord>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[derive(serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct MxRecord {
|
||||
preference: u16,
|
||||
exchange: String,
|
||||
|
@ -42,7 +42,7 @@ impl ToString for ResponseFormat {
|
||||
|
||||
/* The echoip view */
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[derive(serde::Deserialize, serde::Serialize, Clone)]
|
||||
#[serde(untagged)]
|
||||
pub enum View {
|
||||
Dig { query: DigQuery, result: simple_dns::DnsLookupResult },
|
||||
@ -67,6 +67,7 @@ impl View {
|
||||
|
||||
/* The engine itself */
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Engine {
|
||||
pub tera: Tera,
|
||||
pub template_config: Option<Table>,
|
||||
@ -76,7 +77,7 @@ impl Engine {
|
||||
pub async fn render_view(
|
||||
&self,
|
||||
format: ResponseFormat,
|
||||
view: View,
|
||||
view: &View,
|
||||
) -> Response {
|
||||
match format {
|
||||
ResponseFormat::TextHtml => {
|
||||
|
Loading…
Reference in New Issue
Block a user