diff options
author | Ilion Beyst <ilion.beyst@gmail.com> | 2021-12-29 19:56:31 +0100 |
---|---|---|
committer | Ilion Beyst <ilion.beyst@gmail.com> | 2021-12-29 19:56:31 +0100 |
commit | 3eeaab6cec70e7a06a99a1ac2662974f71064bee (patch) | |
tree | 9d5a2665ed32df41b2be131d5e27e8b321ce78a8 /backend/src/routes | |
parent | ee5af8d07625bfc7ad11b842b3941bb095aa6a6e (diff) | |
parent | 1fb4a5151bd8cfe6de4d8c19e2066a9281a0b61a (diff) | |
download | planetwars.dev-3eeaab6cec70e7a06a99a1ac2662974f71064bee.tar.xz planetwars.dev-3eeaab6cec70e7a06a99a1ac2662974f71064bee.zip |
Merge branch 'backend-server'
Diffstat (limited to 'backend/src/routes')
-rw-r--r-- | backend/src/routes/bots.rs | 75 | ||||
-rw-r--r-- | backend/src/routes/mod.rs | 2 | ||||
-rw-r--r-- | backend/src/routes/users.rs | 94 |
3 files changed, 171 insertions, 0 deletions
diff --git a/backend/src/routes/bots.rs b/backend/src/routes/bots.rs new file mode 100644 index 0000000..da09669 --- /dev/null +++ b/backend/src/routes/bots.rs @@ -0,0 +1,75 @@ +use axum::extract::{Path, RawBody}; +use axum::http::StatusCode; +use axum::Json; +use rand::Rng; +use serde::{Deserialize, Serialize}; +use std::io::Cursor; +use std::path; + +use crate::db::bots::{self, CodeBundle}; +use crate::db::users::User; +use crate::DatabaseConnection; +use bots::Bot; + +#[derive(Serialize, Deserialize, Debug)] +pub struct BotParams { + name: String, +} + +pub async fn create_bot( + conn: DatabaseConnection, + user: User, + params: Json<BotParams>, +) -> (StatusCode, Json<Bot>) { + let bot_params = bots::NewBot { + owner_id: user.id, + name: ¶ms.name, + }; + let bot = bots::create_bot(&bot_params, &conn).unwrap(); + (StatusCode::CREATED, Json(bot)) +} + +// TODO: handle errors +pub async fn get_bot(conn: DatabaseConnection, Path(bot_id): Path<i32>) -> Json<Bot> { + let bot = bots::find_bot(bot_id, &conn).unwrap(); + Json(bot) +} + +// TODO: proper error handling +pub async fn upload_bot_code( + conn: DatabaseConnection, + user: User, + Path(bot_id): Path<i32>, + RawBody(body): RawBody, +) -> (StatusCode, Json<CodeBundle>) { + // TODO: put in config somewhere + let data_path = "./data/bots"; + + let bot = bots::find_bot(bot_id, &conn).expect("Bot not found"); + + assert_eq!(user.id, bot.owner_id); + + // generate a random filename + let token: [u8; 16] = rand::thread_rng().gen(); + let name = base64::encode(&token); + + 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(); + + zip::ZipArchive::new(Cursor::new(buf)) + .unwrap() + .extract(&path) + .unwrap(); + + let bundle = bots::NewCodeBundle { + bot_id: bot.id, + path: path.to_str().unwrap(), + }; + let code_bundle = + bots::create_code_bundle(&bundle, &conn).expect("Failed to create code bundle"); + + (StatusCode::CREATED, Json(code_bundle)) +} diff --git a/backend/src/routes/mod.rs b/backend/src/routes/mod.rs new file mode 100644 index 0000000..718d7ef --- /dev/null +++ b/backend/src/routes/mod.rs @@ -0,0 +1,2 @@ +pub mod bots; +pub mod users; diff --git a/backend/src/routes/users.rs b/backend/src/routes/users.rs new file mode 100644 index 0000000..fc77d7b --- /dev/null +++ b/backend/src/routes/users.rs @@ -0,0 +1,94 @@ +use crate::db::users::{Credentials, User}; +use crate::db::{sessions, users}; +use crate::DatabaseConnection; +use axum::extract::{FromRequest, RequestParts, TypedHeader}; +use axum::headers::authorization::Bearer; +use axum::headers::Authorization; +use axum::http::StatusCode; +use axum::{async_trait, Json}; +use serde::{Deserialize, Serialize}; + +type AuthorizationHeader = TypedHeader<Authorization<Bearer>>; + +#[async_trait] +impl<B> FromRequest<B> for User +where + B: Send, +{ + type Rejection = (StatusCode, String); + + async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> { + let conn = DatabaseConnection::from_request(req).await?; + let TypedHeader(Authorization(bearer)) = AuthorizationHeader::from_request(req) + .await + .map_err(|_| (StatusCode::UNAUTHORIZED, "".to_string()))?; + + let (_session, user) = sessions::find_user_by_session(bearer.token(), &conn) + .map_err(|_| (StatusCode::UNAUTHORIZED, "".to_string()))?; + + Ok(user) + } +} + +#[derive(Serialize, Deserialize)] +pub struct UserData { + pub user_id: i32, + pub username: String, +} + +impl From<User> for UserData { + fn from(user: User) -> Self { + UserData { + user_id: user.id, + username: user.username, + } + } +} + +#[derive(Deserialize)] +pub struct RegistrationParams { + pub username: String, + pub password: String, +} + +pub async fn register( + conn: DatabaseConnection, + params: Json<RegistrationParams>, +) -> Json<UserData> { + let credentials = Credentials { + username: ¶ms.username, + password: ¶ms.password, + }; + let user = users::create_user(&credentials, &conn).unwrap(); + Json(user.into()) +} + +#[derive(Deserialize)] +pub struct LoginParams { + pub username: String, + pub password: String, +} + +pub async fn login( + conn: DatabaseConnection, + params: Json<LoginParams>, +) -> Result<String, StatusCode> { + let credentials = Credentials { + username: ¶ms.username, + password: ¶ms.password, + }; + // TODO: handle failures + let authenticated = users::authenticate_user(&credentials, &conn); + + match authenticated { + None => Err(StatusCode::FORBIDDEN), + Some(user) => { + let session = sessions::create_session(&user, &conn); + Ok(session.token) + } + } +} + +pub async fn current_user(user: User) -> Json<UserData> { + Json(user.into()) +} |