This commit is contained in:
parent
2facdcb2ea
commit
c667608fbb
7 changed files with 109 additions and 101 deletions
|
|
@ -9,10 +9,22 @@ pub struct Model {
|
|||
pub id: u32,
|
||||
pub user_id: u32,
|
||||
pub name: String,
|
||||
pub state: BalState,
|
||||
pub start_timestamp: i64,
|
||||
pub end_timestamp: i64
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
#[sea_orm(rs_type = "String", db_type = "String(StringLen::N(1))")]
|
||||
pub enum BalState {
|
||||
#[sea_orm(string_value = "P")]
|
||||
Pending,
|
||||
#[sea_orm(string_value = "O")]
|
||||
Ongoing,
|
||||
#[sea_orm(string_value = "E")]
|
||||
Ended
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::book_instance::Entity")]
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ pub struct Model {
|
|||
pub username: String,
|
||||
pub hashed_password: String,
|
||||
pub owner_id: Option<u32>,
|
||||
pub current_bal_id: Option<u32>,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
|
|
|
|||
|
|
@ -170,9 +170,9 @@ pub async fn run_server(db: Arc<DatabaseConnection>, port: u16, serve_docs: bool
|
|||
.routes(routes!(routes::bal::get_bal_by_id))
|
||||
.routes(routes!(routes::bal::create_bal))
|
||||
.routes(routes!(routes::bal::update_bal))
|
||||
.routes(routes!(routes::bal::start_bal))
|
||||
.routes(routes!(routes::bal::stop_bal))
|
||||
.routes(routes!(routes::bal::get_bals))
|
||||
.routes(routes!(routes::bal::get_current_bal))
|
||||
.routes(routes!(routes::bal::set_current_bal))
|
||||
// Authentication
|
||||
.route_layer(middleware::from_fn_with_state(shared_state.clone(), routes::auth::auth_middleware))
|
||||
.routes(routes!(routes::auth::auth))
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ use std::sync::Arc;
|
|||
|
||||
use axum::{extract::{Path, State}, Json};
|
||||
use reqwest::{StatusCode};
|
||||
use sea_orm::{ActiveModelTrait, ActiveValue::{NotSet, Set}, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, TryIntoModel};
|
||||
use sea_orm::{ActiveModelTrait, ActiveValue::{NotSet, Set}, ColumnTrait, EntityTrait, QueryFilter, TryIntoModel};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::{entities::{bal, book_instance, prelude::*, user}, routes::auth::Claims, AppState};
|
||||
use crate::{entities::{bal::{self, BalState}, prelude::*}, routes::auth::Claims, AppState};
|
||||
|
||||
|
||||
#[derive(IntoParams)]
|
||||
|
|
@ -74,6 +74,7 @@ pub async fn create_bal(
|
|||
id: NotSet,
|
||||
user_id: Set(claims.user_id),
|
||||
name: Set(instance_payload.name),
|
||||
state: Set(BalState::Pending),
|
||||
start_timestamp: Set(instance_payload.start_timestamp),
|
||||
end_timestamp: Set(instance_payload.end_timestamp),
|
||||
};
|
||||
|
|
@ -150,6 +151,98 @@ pub async fn update_bal(
|
|||
}
|
||||
}
|
||||
|
||||
#[axum::debug_handler]
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/bal/{id}/start",
|
||||
params(BalByIdParams),
|
||||
security(("jwt" = [])),
|
||||
responses(
|
||||
(status = OK, body = bal::Model, description = "Successfully started bal"),
|
||||
(status = CONFLICT, description = "The specified BAL was not in a Pending state, cannot start it"),
|
||||
(status = NOT_FOUND, description = "No bal with specified id was found"),
|
||||
(status = FORBIDDEN, description = "You don't own the specified bal"),
|
||||
),
|
||||
summary = "Start a bal",
|
||||
description = "Start a bal",
|
||||
tag = "bal-api",
|
||||
)]
|
||||
pub async fn start_bal(
|
||||
State(state): State<Arc<AppState>>,
|
||||
claims: Claims,
|
||||
Path(id): Path<u32>,
|
||||
) -> (StatusCode, Json<Option<bal::Model>>) {
|
||||
if let Ok(Some(bal)) = Bal::find_by_id(id).one(state.db_conn.as_ref()).await {
|
||||
if bal.user_id != claims.user_id {
|
||||
return (StatusCode::FORBIDDEN, Json(None));
|
||||
}
|
||||
if bal.state != BalState::Pending {
|
||||
return (StatusCode::CONFLICT, Json(None));
|
||||
}
|
||||
let mut bal: bal::ActiveModel = bal.into();
|
||||
bal.state = Set(BalState::Ongoing);
|
||||
|
||||
match bal.update(state.db_conn.as_ref()).await {
|
||||
Err(e) => {
|
||||
log::error!(target: "api", "Error while updating bal from api: {:#?}", e);
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, Json(None))
|
||||
},
|
||||
Ok(res) => {
|
||||
let model = res.try_into_model().expect("All fields should be set once the bal is saved");
|
||||
(StatusCode::OK, Json(Some(model)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(StatusCode::NOT_FOUND, Json(None))
|
||||
}
|
||||
}
|
||||
|
||||
#[axum::debug_handler]
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/bal/{id}/stop",
|
||||
params(BalByIdParams),
|
||||
security(("jwt" = [])),
|
||||
responses(
|
||||
(status = OK, body = bal::Model, description = "Successfully stopped bal"),
|
||||
(status = CONFLICT, description = "The specified BAL was not in an Ongoing state, cannot stop it"),
|
||||
(status = NOT_FOUND, description = "No bal with specified id was found"),
|
||||
(status = FORBIDDEN, description = "You don't own the specified bal"),
|
||||
),
|
||||
summary = "Stop a bal",
|
||||
description = "Stop a bal",
|
||||
tag = "bal-api",
|
||||
)]
|
||||
pub async fn stop_bal(
|
||||
State(state): State<Arc<AppState>>,
|
||||
claims: Claims,
|
||||
Path(id): Path<u32>,
|
||||
) -> (StatusCode, Json<Option<bal::Model>>) {
|
||||
if let Ok(Some(bal)) = Bal::find_by_id(id).one(state.db_conn.as_ref()).await {
|
||||
if bal.user_id != claims.user_id {
|
||||
return (StatusCode::FORBIDDEN, Json(None));
|
||||
}
|
||||
if bal.state != BalState::Ongoing {
|
||||
return (StatusCode::CONFLICT, Json(None));
|
||||
}
|
||||
let mut bal: bal::ActiveModel = bal.into();
|
||||
bal.state = Set(BalState::Ended);
|
||||
|
||||
match bal.update(state.db_conn.as_ref()).await {
|
||||
Err(e) => {
|
||||
log::error!(target: "api", "Error while updating bal from api: {:#?}", e);
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, Json(None))
|
||||
},
|
||||
Ok(res) => {
|
||||
let model = res.try_into_model().expect("All fields should be set once the bal is saved");
|
||||
(StatusCode::OK, Json(Some(model)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(StatusCode::NOT_FOUND, Json(None))
|
||||
}
|
||||
}
|
||||
|
||||
#[axum::debug_handler]
|
||||
#[utoipa::path(
|
||||
get,
|
||||
|
|
@ -173,96 +266,3 @@ pub async fn get_bals(
|
|||
}
|
||||
}
|
||||
|
||||
#[axum::debug_handler]
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/bal/current",
|
||||
security(("jwt" = [])),
|
||||
responses(
|
||||
(status = OK, body = bal::Model, description = "Your current active BAL"),
|
||||
(status = NOT_FOUND, description = "You don't have a currently active BAL"),
|
||||
),
|
||||
summary = "Get your current active BAL",
|
||||
description = "Get your current active BAL",
|
||||
tag = "bal-api",
|
||||
)]
|
||||
pub async fn get_current_bal(
|
||||
State(state): State<Arc<AppState>>,
|
||||
claims: Claims,
|
||||
) -> (StatusCode, Json<Option<bal::Model>>) {
|
||||
if let Ok(Some(user)) = User::find_by_id(claims.user_id).one(state.db_conn.as_ref()).await {
|
||||
if let Some(bal_id) = user.current_bal_id {
|
||||
(StatusCode::OK, Json(Some(Bal::find_by_id(bal_id).one(state.db_conn.as_ref()).await.unwrap().unwrap())))
|
||||
} else {
|
||||
(StatusCode::NOT_FOUND, Json(None))
|
||||
}
|
||||
} else {
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, Json(None))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, PartialEq, utoipa::ToSchema)]
|
||||
pub enum BookInstanceTransferMode {
|
||||
All,
|
||||
None,
|
||||
OwnedOnly
|
||||
}
|
||||
|
||||
#[derive(Deserialize, utoipa::ToSchema)]
|
||||
pub struct SetCurrentBalParams{
|
||||
bal_id: u32,
|
||||
transfer_book_instances: Option<BookInstanceTransferMode>
|
||||
}
|
||||
|
||||
#[axum::debug_handler]
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/bal/current",
|
||||
request_body = SetCurrentBalParams,
|
||||
security(("jwt" = [])),
|
||||
responses(
|
||||
(status = OK, description = "Successfully set current active BAL"),
|
||||
(status = UNAUTHORIZED, description = "Tried to set a BAL you don't own as your active BAL"),
|
||||
),
|
||||
summary = "Set your current active BAL",
|
||||
description = "Set your current active BAL",
|
||||
tag = "bal-api",
|
||||
)]
|
||||
pub async fn set_current_bal(
|
||||
State(state): State<Arc<AppState>>,
|
||||
claims: Claims,
|
||||
Json(payload): Json<SetCurrentBalParams>,
|
||||
) -> StatusCode {
|
||||
if let Ok(Some(new_bal)) = Bal::find_by_id(payload.bal_id).one(state.db_conn.as_ref()).await
|
||||
&& let Ok(Some(user)) = User::find_by_id(claims.user_id).one(state.db_conn.as_ref()).await
|
||||
{
|
||||
if new_bal.user_id != claims.user_id {
|
||||
return StatusCode::UNAUTHORIZED;
|
||||
}
|
||||
|
||||
if let Some(old_bal_id) = user.current_bal_id {
|
||||
// Optional instance transfer
|
||||
if let Some(mode) = payload.transfer_book_instances && mode != BookInstanceTransferMode::None {
|
||||
let mut update_query = BookInstance::update_many()
|
||||
.set(book_instance::ActiveModel {
|
||||
bal_id: Set(new_bal.id),
|
||||
..Default::default()
|
||||
})
|
||||
.filter(book_instance::Column::BalId.eq(old_bal_id));
|
||||
if mode == BookInstanceTransferMode::OwnedOnly {
|
||||
update_query = update_query.filter(book_instance::Column::OwnerId.eq(user.owner_id));
|
||||
}
|
||||
|
||||
let _ = update_query.exec(state.db_conn.as_ref()).await;
|
||||
}
|
||||
}
|
||||
|
||||
// Set current bal on user
|
||||
let mut user_active_model: user::ActiveModel = user.into_active_model();
|
||||
user_active_model.current_bal_id = Set(Some(payload.bal_id));
|
||||
let _ = User::update(user_active_model).exec(state.db_conn.as_ref()).await;
|
||||
StatusCode::OK
|
||||
} else {
|
||||
return StatusCode::NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ use futures_util::{sink::SinkExt, stream::StreamExt};
|
|||
#[utoipa::path(
|
||||
get,
|
||||
path = "/ws",
|
||||
security(("jwt" = [])),
|
||||
responses(
|
||||
(status = SWITCHING_PROTOCOLS, description = "Succesfully reached the websocket, now upgrade to establish the connection"),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ pub async fn manage_users(db: Arc<DatabaseConnection>) {
|
|||
id: NotSet,
|
||||
username: Set(username),
|
||||
hashed_password: Set(hash_password(password)),
|
||||
current_bal_id: Set(None),
|
||||
owner_id: Set(None)
|
||||
};
|
||||
let res = new_user.clone().insert(db.as_ref()).await.unwrap();
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ where C: ConnectionTrait {
|
|||
id: NotSet,
|
||||
username: Set(username.clone()),
|
||||
hashed_password: Set(hash_password(password)),
|
||||
current_bal_id: Set(None),
|
||||
owner_id: Set(None)
|
||||
};
|
||||
let res = new_user.clone().insert(db_conn).await.unwrap();
|
||||
|
|
|
|||
Reference in a new issue