From 0f27ca80fbdb3366e047def628123d3cc8ca051c Mon Sep 17 00:00:00 2001 From: Ilion Beyst Date: Thu, 30 Dec 2021 23:41:47 +0100 Subject: prototype code upload --- planetwars-server/Cargo.toml | 2 +- planetwars-server/src/lib.rs | 5 ++- planetwars-server/src/routes/bots.rs | 59 +++++++++++++++++++++--------------- 3 files changed, 40 insertions(+), 26 deletions(-) (limited to 'planetwars-server') 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)) } -- cgit v1.2.3