diff --git a/planetwars-server/Cargo.toml b/planetwars-server/Cargo.toml index 8fb7693..3a28d5d 100644 --- a/planetwars-server/Cargo.toml +++ b/planetwars-server/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] tokio = { version = "1.15", features = ["full"] } hyper = "0.14" -axum = { version = "0.4", features = ["json", "headers"] } +axum = { version = "0.4", features = ["json", "headers", "multipart"] } diesel = { version = "1.4.4", features = ["postgres", "chrono"] } bb8 = "0.7" bb8-diesel = "0.2" diff --git a/planetwars-server/src/lib.rs b/planetwars-server/src/lib.rs index b3aac5f..46c1100 100644 --- a/planetwars-server/src/lib.rs +++ b/planetwars-server/src/lib.rs @@ -36,7 +36,10 @@ pub async fn api() -> Router { .route("/bots", post(routes::bots::create_bot)) .route("/bots/my_bots", get(routes::bots::get_my_bots)) .route("/bots/:bot_id", get(routes::bots::get_bot)) - .route("/bots/:bot_id/upload", post(routes::bots::upload_bot_code)) + .route( + "/bots/:bot_id/upload", + post(routes::bots::upload_code_multipart), + ) .layer(AddExtensionLayer::new(pool)); api } diff --git a/planetwars-server/src/routes/bots.rs b/planetwars-server/src/routes/bots.rs index 033c683..83127cd 100644 --- a/planetwars-server/src/routes/bots.rs +++ b/planetwars-server/src/routes/bots.rs @@ -1,17 +1,21 @@ -use axum::extract::{Path, RawBody}; +use axum::extract::{Multipart, Path, RawBody}; use axum::http::StatusCode; use axum::response::IntoResponse; use axum::Json; +use rand::distributions::Alphanumeric; use rand::Rng; use serde::{Deserialize, Serialize}; use std::io::Cursor; -use std::path; +use std::path::{self, PathBuf}; use crate::db::bots::{self, CodeBundle}; use crate::db::users::User; use crate::DatabaseConnection; use bots::Bot; +// TODO: make this a parameter +const BOTS_DIR: &str = "./data/bots"; + #[derive(Serialize, Deserialize, Debug)] pub struct BotParams { name: String, @@ -46,41 +50,48 @@ pub async fn get_my_bots( .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) } -// TODO: proper error handling -pub async fn upload_bot_code( +// TODO: currently this only implements the happy flow +pub async fn upload_code_multipart( conn: DatabaseConnection, user: User, Path(bot_id): Path, - RawBody(body): RawBody, -) -> (StatusCode, Json) { - // TODO: put in config somewhere - let data_path = "./data/bots"; + mut multipart: Multipart, +) -> Result, StatusCode> { + let bots_dir = PathBuf::from(BOTS_DIR); - let bot = bots::find_bot(bot_id, &conn).expect("Bot not found"); + let bot = bots::find_bot(bot_id, &conn).map_err(|_| StatusCode::NOT_FOUND)?; - assert_eq!(user.id, bot.owner_id); + if user.id != bot.owner_id { + return Err(StatusCode::FORBIDDEN); + } - // generate a random filename - let token: [u8; 16] = rand::thread_rng().gen(); - let name = base64::encode(&token); + let data = multipart + .next_field() + .await + .map_err(|_| StatusCode::BAD_REQUEST)? + .ok_or(StatusCode::BAD_REQUEST)? + .bytes() + .await + .map_err(|_| StatusCode::BAD_REQUEST)?; - let path = path::Path::new(data_path).join(name); - // let capped_buf = data.open(10usize.megabytes()).into_bytes().await.unwrap(); - // assert!(capped_buf.is_complete()); - // let buf = capped_buf.into_inner(); - let buf = hyper::body::to_bytes(body).await.unwrap(); + // TODO: this random path might be a bit redundant + let folder_name: String = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(16) + .map(char::from) + .collect(); - zip::ZipArchive::new(Cursor::new(buf)) - .unwrap() - .extract(&path) - .unwrap(); + zip::ZipArchive::new(Cursor::new(data)) + .map_err(|_| StatusCode::BAD_REQUEST)? + .extract(bots_dir.join(&folder_name)) + .map_err(|_| StatusCode::BAD_REQUEST)?; let bundle = bots::NewCodeBundle { bot_id: bot.id, - path: path.to_str().unwrap(), + path: &folder_name, }; let code_bundle = bots::create_code_bundle(&bundle, &conn).expect("Failed to create code bundle"); - (StatusCode::CREATED, Json(code_bundle)) + Ok(Json(code_bundle)) } diff --git a/web/pw-server/src/routes/bots/[bot_id].svelte b/web/pw-server/src/routes/bots/[bot_id].svelte index 90fd78d..1d7994d 100644 --- a/web/pw-server/src/routes/bots/[bot_id].svelte +++ b/web/pw-server/src/routes/bots/[bot_id].svelte @@ -6,7 +6,7 @@ const res = await fetch(`/api/bots/${page.params["bot_id"]}`, { headers: { "Content-Type": "application/json", - Authorization: `Bearer ${token}`, + "Authorization": `Bearer ${token}`, }, }); @@ -27,8 +27,36 @@
{bot["name"]}
+ + +
Upload code
+
+ + +
\ No newline at end of file