Compare commits
2 commits
5d4ece3c34
...
61aac4bd80
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61aac4bd80 | ||
|
|
9bbdb9decb |
5 changed files with 86 additions and 9 deletions
|
|
@ -10,6 +10,7 @@ pub struct Model {
|
||||||
#[sea_orm(unique)]
|
#[sea_orm(unique)]
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub hashed_password: String,
|
pub hashed_password: String,
|
||||||
|
pub current_bal_id: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model {
|
impl Model {
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,8 @@ async fn run_server(db: Arc<DatabaseConnection>, port: u16) {
|
||||||
.routes(routes!(routes::bal::create_bal))
|
.routes(routes!(routes::bal::create_bal))
|
||||||
.routes(routes!(routes::bal::update_bal))
|
.routes(routes!(routes::bal::update_bal))
|
||||||
.routes(routes!(routes::bal::get_bals))
|
.routes(routes!(routes::bal::get_bals))
|
||||||
|
.routes(routes!(routes::bal::get_current_bal))
|
||||||
|
.routes(routes!(routes::bal::set_current_bal))
|
||||||
// Authentication
|
// Authentication
|
||||||
.route_layer(middleware::from_fn_with_state(shared_state.clone(), routes::auth::auth_middleware))
|
.route_layer(middleware::from_fn_with_state(shared_state.clone(), routes::auth::auth_middleware))
|
||||||
.routes(routes!(routes::auth::auth))
|
.routes(routes!(routes::auth::auth))
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use axum::{extract::{Path, State}, Json};
|
use axum::{extract::{Path, State}, Json};
|
||||||
use reqwest::{StatusCode};
|
use reqwest::{StatusCode};
|
||||||
use sea_orm::{ActiveModelTrait, ActiveValue::{NotSet, Set}, ColumnTrait, EntityTrait, QueryFilter, TryIntoModel};
|
use sea_orm::{ActiveModelTrait, ActiveValue::{NotSet, Set}, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, TryIntoModel};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use utoipa::IntoParams;
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
use crate::{entities::{bal, prelude::*}, routes::auth::Claims, AppState};
|
use crate::{entities::{bal, prelude::*, user}, routes::auth::Claims, utils::auth::user_is_bal_owner, AppState};
|
||||||
|
|
||||||
|
|
||||||
#[derive(IntoParams)]
|
#[derive(IntoParams)]
|
||||||
|
|
@ -159,3 +159,68 @@ pub async fn get_bals(
|
||||||
(StatusCode::NOT_FOUND, Json(vec![]))
|
(StatusCode::NOT_FOUND, Json(vec![]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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, utoipa::ToSchema)]
|
||||||
|
pub struct BalIdParams{
|
||||||
|
id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[axum::debug_handler]
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/bal/current",
|
||||||
|
request_body = BalIdParams,
|
||||||
|
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<BalIdParams>,
|
||||||
|
) -> StatusCode {
|
||||||
|
if !user_is_bal_owner(claims.user_id, payload.id, state.db_conn.as_ref()).await {
|
||||||
|
return StatusCode::UNAUTHORIZED;
|
||||||
|
}
|
||||||
|
if let Ok(Some(user)) = User::find_by_id(claims.user_id).one(state.db_conn.as_ref()).await {
|
||||||
|
let mut user_active_model: user::ActiveModel = user.into_active_model();
|
||||||
|
user_active_model.current_bal_id = Set(Some(payload.id));
|
||||||
|
let _ = User::update(user_active_model).exec(state.db_conn.as_ref()).await;
|
||||||
|
StatusCode::OK
|
||||||
|
} else {
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,8 @@ pub async fn get_bal_book_instances_by_ean(
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, utoipa::ToSchema)]
|
#[derive(Deserialize, Serialize, utoipa::ToSchema)]
|
||||||
pub struct BookInstanceSearchParams {
|
pub struct BookInstanceSearchParams {
|
||||||
title: String,
|
title: Option<String>,
|
||||||
|
author: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, utoipa::ToSchema)]
|
#[derive(Serialize, utoipa::ToSchema)]
|
||||||
|
|
@ -360,17 +361,24 @@ pub async fn search_bal_book_instances(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
Path(bal_id): Path<u32>,
|
Path(bal_id): Path<u32>,
|
||||||
Json(instance_payload): Json<BookInstanceSearchParams>,
|
Json(payload): Json<BookInstanceSearchParams>,
|
||||||
) -> (StatusCode, Json<Option<BookInstanceSearchResults>>) {
|
) -> (StatusCode, Json<Option<BookInstanceSearchResults>>) {
|
||||||
if !user_is_bal_owner(claims.user_id, bal_id, state.db_conn.as_ref()).await {
|
if !user_is_bal_owner(claims.user_id, bal_id, state.db_conn.as_ref()).await {
|
||||||
return (StatusCode::FORBIDDEN, Json(None));
|
return (StatusCode::FORBIDDEN, Json(None));
|
||||||
}
|
}
|
||||||
if let Ok(res) = BookInstance::find()
|
|
||||||
|
let mut search_query = BookInstance::find()
|
||||||
.filter(book_instance::Column::BalId.eq(bal_id))
|
.filter(book_instance::Column::BalId.eq(bal_id))
|
||||||
.filter(book_instance::Column::Available.eq(true))
|
.filter(book_instance::Column::Available.eq(true))
|
||||||
.join(JoinType::InnerJoin, book_instance::Relation::Book.def())
|
.join(JoinType::InnerJoin, book_instance::Relation::Book.def());
|
||||||
.filter(book::Column::Title.like(format!("%{}%", instance_payload.title)))
|
if let Some(title) = payload.title {
|
||||||
.all(state.db_conn.as_ref()).await
|
search_query = search_query.filter(book::Column::Title.like(format!("%{}%", title)))
|
||||||
|
}
|
||||||
|
if let Some(author) = payload.author {
|
||||||
|
search_query = search_query.filter(book::Column::Author.like(format!("%{}%", author)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(res) = search_query.all(state.db_conn.as_ref()).await
|
||||||
{
|
{
|
||||||
let mut book_id_map = HashMap::new();
|
let mut book_id_map = HashMap::new();
|
||||||
for instance in &res {
|
for instance in &res {
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,8 @@ pub async fn manage_users(db: Arc<DatabaseConnection>) {
|
||||||
let new_user = user::ActiveModel {
|
let new_user = user::ActiveModel {
|
||||||
id: NotSet,
|
id: NotSet,
|
||||||
username: Set(username),
|
username: Set(username),
|
||||||
hashed_password: Set(hash_password(password))
|
hashed_password: Set(hash_password(password)),
|
||||||
|
current_bal_id: Set(None)
|
||||||
};
|
};
|
||||||
let res = new_user.insert(db.as_ref()).await.unwrap();
|
let res = new_user.insert(db.as_ref()).await.unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
Reference in a new issue