use std::net::SocketAddr; use axum::extract::{ConnectInfo, Path}; use axum::http::HeaderMap; use axum::http::StatusCode; use axum::response::{Html, IntoResponse}; use axum::Extension; use info_utils::prelude::*; use crate::ServerState; use crate::UrlRow; pub async fn index() -> Html<&'static str> { Html("hello, world!") } /// # Panics /// Will panic if `parse()` fails pub async fn id( headers: HeaderMap, ConnectInfo(addr): ConnectInfo, Extension(state): Extension, Path(id): Path, ) -> impl IntoResponse { let mut show_request = false; log!("Request for '{}' from {}", id.clone(), addr.ip()); let mut use_id = id; if use_id.ends_with('+') { show_request = true; use_id.pop(); } let item: Result = sqlx::query_as("SELECT * FROM chela.urls WHERE id = $1") .bind(use_id) .fetch_one(&state.db_pool) .await; if let Ok(it) = item { if url::Url::parse(&it.url).is_ok() { if show_request { return Html(format!( r#"
http://{}/{} -> {}
"#, state.host, it.id, it.url, it.url )) .into_response(); } log!("Redirecting {} -> {}", it.id, it.url); save_analytics(headers, it.clone(), addr, state).await; let mut response_headers = HeaderMap::new(); response_headers.insert("Cache-Control", "private, max-age=90".parse().unwrap()); response_headers.insert("Location", it.url.parse().unwrap()); return ( StatusCode::MOVED_PERMANENTLY, response_headers, Html(format!( r#"Redirecting to {}"#, it.url, it.url )), ) .into_response(); } } else if let Err(err) = item { warn!("{}", err); return ( StatusCode::INTERNAL_SERVER_ERROR, Html(format!("
Internal error: {err}.
")), ) .into_response(); } (StatusCode::NOT_FOUND, Html("
Not found.
")).into_response() } async fn save_analytics(headers: HeaderMap, item: UrlRow, addr: SocketAddr, state: ServerState) { let id = item.id; let ip = addr.ip().to_string(); let referer = match headers.get("referer") { Some(it) => { if let Ok(i) = it.to_str() { Some(i) } else { None } } None => None, }; let user_agent = match headers.get("user-agent") { Some(it) => { if let Ok(i) = it.to_str() { Some(i) } else { None } } None => None, }; let res = sqlx::query( " INSERT INTO chela.tracking (id,ip,referrer,user_agent) VALUES ($1,$2,$3,$4) ", ) .bind(id.clone()) .bind(ip.clone()) .bind(referer) .bind(user_agent) .execute(&state.db_pool) .await; if res.is_ok() { log!("Saved analytics for '{id}' from {ip}"); } } pub async fn create_id(Extension(state): Extension) -> Html { Html(format!( r#" {} URL Shortener
"#, state.host )) }