127 lines
3.6 KiB
Rust
127 lines
3.6 KiB
Rust
mod auth;
|
|
mod bucket_cors;
|
|
pub mod ratelimit;
|
|
pub mod session;
|
|
pub(crate) mod sha_body;
|
|
|
|
pub use auth::auth_layer;
|
|
pub use bucket_cors::bucket_cors_layer;
|
|
pub use ratelimit::{rate_limit_layer, RateLimitLayerState};
|
|
pub use session::{csrf_layer, session_layer, SessionHandle, SessionLayerState};
|
|
|
|
use axum::extract::{Request, State};
|
|
use axum::middleware::Next;
|
|
use axum::response::Response;
|
|
use std::time::Instant;
|
|
|
|
use crate::state::AppState;
|
|
|
|
pub async fn server_header(req: Request, next: Next) -> Response {
|
|
let mut resp = next.run(req).await;
|
|
resp.headers_mut()
|
|
.insert("server", crate::SERVER_HEADER.parse().unwrap());
|
|
resp
|
|
}
|
|
|
|
pub async fn request_log_layer(req: Request, next: Next) -> Response {
|
|
let start = Instant::now();
|
|
let method = req.method().clone();
|
|
let uri = req.uri().clone();
|
|
let version = req.version();
|
|
let remote = req
|
|
.extensions()
|
|
.get::<axum::extract::ConnectInfo<std::net::SocketAddr>>()
|
|
.map(|ci| ci.0.ip().to_string())
|
|
.unwrap_or_else(|| "-".to_string());
|
|
|
|
let response = next.run(req).await;
|
|
|
|
let status = response.status().as_u16();
|
|
let elapsed_ms = start.elapsed().as_secs_f64() * 1000.0;
|
|
let bytes_out = response
|
|
.headers()
|
|
.get(axum::http::header::CONTENT_LENGTH)
|
|
.and_then(|v| v.to_str().ok())
|
|
.and_then(|v| v.parse::<u64>().ok());
|
|
|
|
tracing::info!(
|
|
target: "myfsio::access",
|
|
remote = %remote,
|
|
method = %method,
|
|
uri = %uri,
|
|
version = ?version,
|
|
status,
|
|
bytes_out = bytes_out.unwrap_or(0),
|
|
elapsed_ms = format!("{:.3}", elapsed_ms),
|
|
"request"
|
|
);
|
|
|
|
response
|
|
}
|
|
|
|
pub async fn ui_metrics_layer(State(state): State<AppState>, req: Request, next: Next) -> Response {
|
|
let metrics = match state.metrics.clone() {
|
|
Some(m) => m,
|
|
None => return next.run(req).await,
|
|
};
|
|
let start = Instant::now();
|
|
let method = req.method().clone();
|
|
let path = req.uri().path().to_string();
|
|
let endpoint_type = classify_ui_endpoint(&path);
|
|
let bytes_in = req
|
|
.headers()
|
|
.get(axum::http::header::CONTENT_LENGTH)
|
|
.and_then(|v| v.to_str().ok())
|
|
.and_then(|v| v.parse::<u64>().ok())
|
|
.unwrap_or(0);
|
|
|
|
let response = next.run(req).await;
|
|
|
|
let latency_ms = start.elapsed().as_secs_f64() * 1000.0;
|
|
let status = response.status().as_u16();
|
|
let bytes_out = response
|
|
.headers()
|
|
.get(axum::http::header::CONTENT_LENGTH)
|
|
.and_then(|v| v.to_str().ok())
|
|
.and_then(|v| v.parse::<u64>().ok())
|
|
.unwrap_or(0);
|
|
let error_code = if status >= 400 { Some("UIError") } else { None };
|
|
metrics.record_request(
|
|
method.as_str(),
|
|
endpoint_type,
|
|
status,
|
|
latency_ms,
|
|
bytes_in,
|
|
bytes_out,
|
|
error_code,
|
|
);
|
|
|
|
response
|
|
}
|
|
|
|
fn classify_ui_endpoint(path: &str) -> &'static str {
|
|
if path.contains("/upload") {
|
|
"ui_upload"
|
|
} else if path.starts_with("/ui/buckets/") {
|
|
"ui_bucket"
|
|
} else if path.starts_with("/ui/iam") {
|
|
"ui_iam"
|
|
} else if path.starts_with("/ui/sites") {
|
|
"ui_sites"
|
|
} else if path.starts_with("/ui/connections") {
|
|
"ui_connections"
|
|
} else if path.starts_with("/ui/metrics") {
|
|
"ui_metrics"
|
|
} else if path.starts_with("/ui/system") {
|
|
"ui_system"
|
|
} else if path.starts_with("/ui/website-domains") {
|
|
"ui_website_domains"
|
|
} else if path.starts_with("/ui/replication") {
|
|
"ui_replication"
|
|
} else if path.starts_with("/login") || path.starts_with("/logout") {
|
|
"ui_auth"
|
|
} else {
|
|
"ui_other"
|
|
}
|
|
}
|