use axum::{ extract::{Form, Path, State}, http::HeaderMap, http::StatusCode, response::{Html, IntoResponse, Response}, }; use std::{path::PathBuf, sync::Arc}; use tokio::fs; use uuid::Uuid; #[derive(Clone)] pub struct AppState { pub paste_dir: Arc, } #[derive(serde::Deserialize)] pub struct PasteForm { body: String, } pub async fn index() -> Html<&'static str> { Html(include_str!("../templates/index.html")) } pub async fn create_paste( State(state): State, headers: HeaderMap, Form(form): Form, ) -> Response { let ip = headers .get("x-real-ip") .and_then(|v| v.to_str().ok()) .unwrap_or("unknown"); let id = Uuid::new_v4().to_string()[..8].to_string(); let path = state.paste_dir.join(&id); fs::write(&path, &form.body).await.unwrap(); tracing::info!( "paste created: id={} size={} bytes ip={}", id, &form.body.len(), ip ); (StatusCode::SEE_OTHER, [("location", format!("/{id}"))]).into_response() } pub async fn get_paste(State(state): State, Path(id): Path) -> Response { let path = state.paste_dir.join(&id); match fs::read_to_string(&path).await { Ok(content) => Html(format!( include_str!("../templates/paste.html"), id = id, content = content )) .into_response(), Err(_) => (StatusCode::NOT_FOUND, "paste not found").into_response(), } } pub async fn get_paste_raw(State(state): State, Path(id): Path) -> Response { let path = state.paste_dir.join(&id); match fs::read_to_string(&path).await { Ok(content) => ([("content-type", "text/plain; charset=utf-8")], content).into_response(), Err(_) => (StatusCode::NOT_FOUND, "paste not found").into_response(), } }