Server: - GET /v1/whoami returns client IP, IPv4/IPv6 classification - Detects proxy via X-Forwarded-For, X-Real-IP, Via headers - Shows proxy details when behind reverse proxy (Caddy etc) - ConnectInfo enabled for direct socket address Web client: - /myip, /whatsmyip, /ip — shows your IP + proxy info - Useful for testing IPv4/IPv6 connectivity Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
67 lines
2.0 KiB
Rust
67 lines
2.0 KiB
Rust
use axum::{extract::ConnectInfo, http::HeaderMap, routing::get, Json, Router};
|
|
use serde_json::json;
|
|
use std::net::SocketAddr;
|
|
|
|
use crate::state::AppState;
|
|
|
|
pub fn routes() -> Router<AppState> {
|
|
Router::new()
|
|
.route("/health", get(health))
|
|
.route("/whoami", get(whoami))
|
|
}
|
|
|
|
async fn health() -> Json<serde_json::Value> {
|
|
Json(json!({ "status": "ok", "version": env!("CARGO_PKG_VERSION") }))
|
|
}
|
|
|
|
async fn whoami(
|
|
headers: HeaderMap,
|
|
connect_info: Option<ConnectInfo<SocketAddr>>,
|
|
) -> Json<serde_json::Value> {
|
|
// Prefer X-Forwarded-For (set by Caddy/reverse proxy), then X-Real-Ip, then direct
|
|
let forwarded = headers
|
|
.get("x-forwarded-for")
|
|
.and_then(|v| v.to_str().ok())
|
|
.map(|v| v.split(',').next().unwrap_or("").trim().to_string());
|
|
|
|
let real_ip = headers
|
|
.get("x-real-ip")
|
|
.and_then(|v| v.to_str().ok())
|
|
.map(|s| s.to_string());
|
|
|
|
let direct = connect_info.map(|ci| ci.0.ip().to_string());
|
|
|
|
let ip = forwarded.clone()
|
|
.or(real_ip.clone())
|
|
.or(direct.clone())
|
|
.unwrap_or_else(|| "unknown".to_string());
|
|
|
|
// Classify as IPv4 or IPv6
|
|
let is_v6 = ip.contains(':');
|
|
|
|
let via = headers.get("via").and_then(|v| v.to_str().ok()).map(|s| s.to_string());
|
|
let proto = headers.get("x-forwarded-proto").and_then(|v| v.to_str().ok()).map(|s| s.to_string());
|
|
let host = headers.get("x-forwarded-host").and_then(|v| v.to_str().ok()).map(|s| s.to_string());
|
|
let behind_proxy = forwarded.is_some() || real_ip.is_some() || via.is_some();
|
|
|
|
let mut result = json!({
|
|
"ip": ip,
|
|
"version": if is_v6 { "IPv6" } else { "IPv4" },
|
|
"direct": direct,
|
|
"behind_proxy": behind_proxy,
|
|
});
|
|
|
|
if behind_proxy {
|
|
let proxy = json!({
|
|
"x_forwarded_for": forwarded,
|
|
"x_real_ip": real_ip,
|
|
"x_forwarded_proto": proto,
|
|
"x_forwarded_host": host,
|
|
"via": via,
|
|
});
|
|
result.as_object_mut().unwrap().insert("proxy".to_string(), proxy);
|
|
}
|
|
|
|
Json(result)
|
|
}
|