diff --git a/Cargo.toml b/Cargo.toml index b08d54b..c315a5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -axum = { version = "0.6.12", features = ["tokio"] } +anyhow = "1.0.70" +axum = { version = "0.6.12", features = ["tokio", "multipart"] } +axum-macros = "0.3.7" +log = "0.4.17" +pretty_env_logger = "0.4.0" +reqwest = { version = "0.11.16", features = ["json", "multipart"] } +serde = { version = "1.0.159", features = ["derive"] } +serde_json = "1.0.95" tokio = { version = "1.27.0", features = ["full"] } diff --git a/src/main.rs b/src/main.rs index 472dd26..479766e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,16 @@ -use axum::{routing::get, Router}; +use axum::extract::{Multipart, Query}; +use axum::http::StatusCode; +use axum::response::{IntoResponse, Response}; +use axum::routing::post; +use axum::{Json, Router}; +use axum_macros::debug_handler; +use serde::{Deserialize, Serialize}; #[tokio::main] async fn main() { + pretty_env_logger::init(); // build our application with a single route - let app = Router::new().route("/", get(|| async { "Hello, World!" })); + let app = Router::new().route("/", post(upload_file)); // run it with hyper on localhost:3000 axum::Server::bind(&"0.0.0.0:6679".parse().unwrap()) @@ -11,3 +18,106 @@ async fn main() { .await .unwrap(); } + +#[derive(Deserialize)] +struct Options { + threshold: String, +} + +#[derive(Serialize, Deserialize)] +#[serde(untagged)] +enum WrappedResponse { + Tags(Vec), + Error(String), +} + +// Make our own error that wraps `anyhow::Error`. +struct AppError(anyhow::Error); + +// Tell axum how to convert `AppError` into a response. +impl IntoResponse for AppError { + fn into_response(self) -> Response { + println!("amogus"); + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Something went wrong: {}", self.0), + ) + .into_response() + } +} + +// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into +// `Result<_, AppError>`. That way you don't need to do that manually. +impl From for AppError +where + E: Into, +{ + fn from(err: E) -> Self { + Self(err.into()) + } +} + +fn try_thing() -> Result<(), anyhow::Error> { + anyhow::bail!("it failed!") +} + +async fn test_handler() -> Result<(), AppError> { + try_thing()?; + Ok(()) +} + +#[debug_handler] +async fn upload_file( + options: Query, + mut multipart: Multipart, +) -> Result<(StatusCode, Json), AppError> { + let mut maybe_file_contents: Option = None; + let mut maybe_file_type: Option = None; + let mut maybe_file_name: Option = None; + + while let Some(field) = multipart.next_field().await.unwrap() { + let name = field.name().unwrap().to_string(); + let content_type = field.content_type().unwrap().to_string(); + let filename = field.file_name().unwrap().to_string(); + let data = field.bytes().await.unwrap(); + + log::info!("Length of `{}` is {} bytes", name, data.len()); + if name == "file" { + maybe_file_contents = Some(data); + maybe_file_type = Some(content_type); + maybe_file_name = Some(filename); + } + } + + if let Some(file_contents) = maybe_file_contents { + let part = reqwest::multipart::Part::bytes(file_contents.to_vec()) + .file_name(maybe_file_name.unwrap()) + .mime_str(maybe_file_type.unwrap().as_ref()) + .unwrap(); + let form = reqwest::multipart::Form::new().part("file", part); + + log::debug!("calling dd"); + + let resp = reqwest::Client::new() + .post("http://localhost:4443") + .multipart(form) + .header("authorization", "Bearer 123") + .query(&[("threshold", options.threshold.clone())]) + .send() + .await?; + + let body = resp.text().await?; + log::info!("body: {}", &body); + let json_response: WrappedResponse = serde_json::from_str(&body)?; + + log::debug!("called!"); + Ok((StatusCode::OK, Json(json_response))) + } else { + Ok(( + StatusCode::INTERNAL_SERVER_ERROR, + Json(WrappedResponse::Error( + "no file found in request".to_string(), + )), + )) + } +}