use chrono::{DateTime, Utc};
use serde_json::Value;
use uuid::Uuid;
use super::{Event, Origin, Outcome};
pub fn prepare(e: &mut Event) {
if e.id.is_empty() {
e.id = Uuid::new_v4().to_string();
}
if e.occurred_at == DateTime::<Utc>::from_timestamp(0, 0).expect("epoch is valid") {
e.occurred_at = Utc::now();
}
}
pub fn outcome_from_http_status(status: u16) -> Outcome {
if status == 0 {
return Outcome {
status: "error".to_string(),
code: 0,
message: Some(Value::String("no response written".to_string())),
};
}
let s = match status {
200..=399 => "ok",
401 | 403 => "denied",
_ => "error",
};
Outcome {
status: s.to_string(),
code: i32::from(status),
message: None,
}
}
pub fn origin_from_headers<F>(get: F, remote_addr: &str) -> Origin
where
F: Fn(&str) -> Option<String>,
{
Origin {
ip: client_ip(&get, remote_addr),
user_agent: header(&get, "user-agent"),
request_id: header(&get, "x-request-id"),
}
}
pub fn client_ip<F>(get: F, remote_addr: &str) -> String
where
F: Fn(&str) -> Option<String>,
{
if let Some(xff) = header_nonempty(&get, "x-forwarded-for") {
let first = xff.split(',').next().unwrap_or("").trim();
return first.to_string();
}
if let Some(xri) = header_nonempty(&get, "x-real-ip") {
return xri;
}
if remote_addr.is_empty() {
return String::new();
}
match remote_addr.rfind(':') {
Some(i) => remote_addr[..i].to_string(),
None => remote_addr.to_string(),
}
}
fn header<F: Fn(&str) -> Option<String>>(get: &F, name: &str) -> String {
get(name).unwrap_or_default()
}
fn header_nonempty<F: Fn(&str) -> Option<String>>(get: &F, name: &str) -> Option<String> {
get(name).filter(|s| !s.is_empty())
}