Can create a movement
This commit is contained in:
		@@ -5,6 +5,7 @@ use std::error::Error;
 | 
				
			|||||||
pub mod accounts_controller;
 | 
					pub mod accounts_controller;
 | 
				
			||||||
pub mod auth_controller;
 | 
					pub mod auth_controller;
 | 
				
			||||||
pub mod files_controller;
 | 
					pub mod files_controller;
 | 
				
			||||||
 | 
					pub mod movement_controller;
 | 
				
			||||||
pub mod server_controller;
 | 
					pub mod server_controller;
 | 
				
			||||||
pub mod static_controller;
 | 
					pub mod static_controller;
 | 
				
			||||||
pub mod tokens_controller;
 | 
					pub mod tokens_controller;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								moneymgr_backend/src/controllers/movement_controller.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								moneymgr_backend/src/controllers/movement_controller.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					use crate::controllers::HttpResult;
 | 
				
			||||||
 | 
					use crate::extractors::auth_extractor::AuthExtractor;
 | 
				
			||||||
 | 
					use crate::services::movements_service;
 | 
				
			||||||
 | 
					use crate::services::movements_service::UpdateMovementQuery;
 | 
				
			||||||
 | 
					use actix_web::{HttpResponse, web};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Create a new movement
 | 
				
			||||||
 | 
					pub async fn create(auth: AuthExtractor, req: web::Json<UpdateMovementQuery>) -> HttpResult {
 | 
				
			||||||
 | 
					    if let Some(err) = req.check_error(auth.user_id()).await? {
 | 
				
			||||||
 | 
					        return Ok(HttpResponse::BadRequest().json(err));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    movements_service::create(&req).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(HttpResponse::Created().finish())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -41,6 +41,7 @@ pub struct ServerConstraints {
 | 
				
			|||||||
    pub token_ip_net: LenConstraints,
 | 
					    pub token_ip_net: LenConstraints,
 | 
				
			||||||
    pub token_max_inactivity: LenConstraints,
 | 
					    pub token_max_inactivity: LenConstraints,
 | 
				
			||||||
    pub account_name: LenConstraints,
 | 
					    pub account_name: LenConstraints,
 | 
				
			||||||
 | 
					    pub movement_label: LenConstraints,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Default for ServerConstraints {
 | 
					impl Default for ServerConstraints {
 | 
				
			||||||
@@ -50,6 +51,7 @@ impl Default for ServerConstraints {
 | 
				
			|||||||
            token_ip_net: LenConstraints::max_only(44),
 | 
					            token_ip_net: LenConstraints::max_only(44),
 | 
				
			||||||
            token_max_inactivity: LenConstraints::new(3600, 3600 * 24 * 365),
 | 
					            token_max_inactivity: LenConstraints::new(3600, 3600 * 24 * 365),
 | 
				
			||||||
            account_name: LenConstraints::not_empty(50),
 | 
					            account_name: LenConstraints::not_empty(50),
 | 
				
			||||||
 | 
					            movement_label: LenConstraints::not_empty(200),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -129,6 +129,8 @@ async fn main() -> std::io::Result<()> {
 | 
				
			|||||||
                "/api/file/{file_id}",
 | 
					                "/api/file/{file_id}",
 | 
				
			||||||
                web::delete().to(files_controller::delete),
 | 
					                web::delete().to(files_controller::delete),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					            // Movement controller
 | 
				
			||||||
 | 
					            .route("/api/movement", web::post().to(movement_controller::create))
 | 
				
			||||||
            // Static assets
 | 
					            // Static assets
 | 
				
			||||||
            .route("/", web::get().to(static_controller::root_index))
 | 
					            .route("/", web::get().to(static_controller::root_index))
 | 
				
			||||||
            .route(
 | 
					            .route(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
pub mod accounts;
 | 
					pub mod accounts;
 | 
				
			||||||
pub mod files;
 | 
					pub mod files;
 | 
				
			||||||
 | 
					pub mod movements;
 | 
				
			||||||
pub mod tokens;
 | 
					pub mod tokens;
 | 
				
			||||||
pub mod users;
 | 
					pub mod users;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										47
									
								
								moneymgr_backend/src/models/movements.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								moneymgr_backend/src/models/movements.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					use crate::models::accounts::AccountID;
 | 
				
			||||||
 | 
					use crate::models::files::FileID;
 | 
				
			||||||
 | 
					use crate::schema::*;
 | 
				
			||||||
 | 
					use diesel::{Insertable, Queryable};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
 | 
				
			||||||
 | 
					pub struct MovementID(pub i32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Queryable, Debug, Clone, serde::Serialize)]
 | 
				
			||||||
 | 
					pub struct Movement {
 | 
				
			||||||
 | 
					    id: i32,
 | 
				
			||||||
 | 
					    account_id: i32,
 | 
				
			||||||
 | 
					    pub time: i64,
 | 
				
			||||||
 | 
					    pub label: String,
 | 
				
			||||||
 | 
					    file_id: Option<i32>,
 | 
				
			||||||
 | 
					    pub amount: f32,
 | 
				
			||||||
 | 
					    pub checked: bool,
 | 
				
			||||||
 | 
					    pub time_create: i64,
 | 
				
			||||||
 | 
					    pub time_update: i64,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Movement {
 | 
				
			||||||
 | 
					    pub fn id(&self) -> MovementID {
 | 
				
			||||||
 | 
					        MovementID(self.id)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn account_id(&self) -> AccountID {
 | 
				
			||||||
 | 
					        AccountID(self.account_id)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn file_id(&self) -> Option<FileID> {
 | 
				
			||||||
 | 
					        self.file_id.map(FileID)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Insertable, Debug)]
 | 
				
			||||||
 | 
					#[diesel(table_name = movements)]
 | 
				
			||||||
 | 
					pub struct NewMovement<'a> {
 | 
				
			||||||
 | 
					    pub account_id: i32,
 | 
				
			||||||
 | 
					    pub time: i64,
 | 
				
			||||||
 | 
					    pub label: &'a str,
 | 
				
			||||||
 | 
					    pub file_id: Option<i32>,
 | 
				
			||||||
 | 
					    pub amount: f32,
 | 
				
			||||||
 | 
					    pub checked: bool,
 | 
				
			||||||
 | 
					    pub time_create: i64,
 | 
				
			||||||
 | 
					    pub time_update: i64,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
pub mod accounts_service;
 | 
					pub mod accounts_service;
 | 
				
			||||||
pub mod files_service;
 | 
					pub mod files_service;
 | 
				
			||||||
 | 
					pub mod movements_service;
 | 
				
			||||||
pub mod tokens_service;
 | 
					pub mod tokens_service;
 | 
				
			||||||
pub mod users_service;
 | 
					pub mod users_service;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										89
									
								
								moneymgr_backend/src/services/movements_service.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								moneymgr_backend/src/services/movements_service.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					use crate::connections::db_connection::db;
 | 
				
			||||||
 | 
					use crate::controllers::server_controller::ServerConstraints;
 | 
				
			||||||
 | 
					use crate::models::accounts::AccountID;
 | 
				
			||||||
 | 
					use crate::models::files::FileID;
 | 
				
			||||||
 | 
					use crate::models::movements::{Movement, MovementID, NewMovement};
 | 
				
			||||||
 | 
					use crate::models::users::UserID;
 | 
				
			||||||
 | 
					use crate::schema::movements;
 | 
				
			||||||
 | 
					use crate::services::{accounts_service, files_service};
 | 
				
			||||||
 | 
					use crate::utils::time_utils::time;
 | 
				
			||||||
 | 
					use diesel::RunQueryDsl;
 | 
				
			||||||
 | 
					use diesel::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(serde::Deserialize)]
 | 
				
			||||||
 | 
					pub struct UpdateMovementQuery {
 | 
				
			||||||
 | 
					    pub account_id: AccountID,
 | 
				
			||||||
 | 
					    pub time: u64,
 | 
				
			||||||
 | 
					    pub label: String,
 | 
				
			||||||
 | 
					    pub file_id: Option<FileID>,
 | 
				
			||||||
 | 
					    pub amount: f32,
 | 
				
			||||||
 | 
					    pub checked: bool,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl UpdateMovementQuery {
 | 
				
			||||||
 | 
					    pub async fn check_error(&self, user_id: UserID) -> anyhow::Result<Option<&'static str>> {
 | 
				
			||||||
 | 
					        let constraints = ServerConstraints::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Check movement label
 | 
				
			||||||
 | 
					        if !constraints.movement_label.check_str(&self.label) {
 | 
				
			||||||
 | 
					            return Ok(Some("Invalid movement label length!"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Check the account
 | 
				
			||||||
 | 
					        let account = accounts_service::get_by_id(self.account_id).await?;
 | 
				
			||||||
 | 
					        if account.user_id() != user_id {
 | 
				
			||||||
 | 
					            return Ok(Some("The user does not own the account!"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Check the file
 | 
				
			||||||
 | 
					        if let Some(file_id) = self.file_id {
 | 
				
			||||||
 | 
					            let file = files_service::get_file_with_id(file_id)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if file.user_id() != user_id {
 | 
				
			||||||
 | 
					                return Ok(Some("The user does not own the referenced file!"));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO : check for conflict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(None)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Create a new movement
 | 
				
			||||||
 | 
					pub async fn create(query: &UpdateMovementQuery) -> anyhow::Result<Movement> {
 | 
				
			||||||
 | 
					    let new_account = NewMovement {
 | 
				
			||||||
 | 
					        account_id: query.account_id.0,
 | 
				
			||||||
 | 
					        time: query.time as i64,
 | 
				
			||||||
 | 
					        label: &query.label,
 | 
				
			||||||
 | 
					        file_id: query.file_id.map(|f| f.0),
 | 
				
			||||||
 | 
					        amount: query.amount,
 | 
				
			||||||
 | 
					        checked: query.checked,
 | 
				
			||||||
 | 
					        time_create: time() as i64,
 | 
				
			||||||
 | 
					        time_update: time() as i64,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let res: Movement = diesel::insert_into(movements::table)
 | 
				
			||||||
 | 
					        .values(&new_account)
 | 
				
			||||||
 | 
					        .get_result(&mut db()?)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    update(res.id(), query).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(res)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Update a movement
 | 
				
			||||||
 | 
					pub async fn update(id: MovementID, q: &UpdateMovementQuery) -> anyhow::Result<()> {
 | 
				
			||||||
 | 
					    diesel::update(movements::dsl::movements.filter(movements::dsl::id.eq(id.0)))
 | 
				
			||||||
 | 
					        .set((
 | 
				
			||||||
 | 
					            movements::dsl::time_update.eq(time() as i64),
 | 
				
			||||||
 | 
					            movements::dsl::account_id.eq(q.account_id.0),
 | 
				
			||||||
 | 
					            movements::dsl::time.eq(q.time as i64),
 | 
				
			||||||
 | 
					            movements::dsl::label.eq(&q.label),
 | 
				
			||||||
 | 
					            movements::dsl::file_id.eq(&q.file_id.map(|f| f.0)),
 | 
				
			||||||
 | 
					            movements::dsl::amount.eq(q.amount),
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					        .execute(&mut db()?)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -17,6 +17,7 @@ export interface ServerConstraints {
 | 
				
			|||||||
  token_name: LenConstraint;
 | 
					  token_name: LenConstraint;
 | 
				
			||||||
  token_ip_net: LenConstraint;
 | 
					  token_ip_net: LenConstraint;
 | 
				
			||||||
  token_max_inactivity: LenConstraint;
 | 
					  token_max_inactivity: LenConstraint;
 | 
				
			||||||
 | 
					  movement_label: LenConstraint;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface LenConstraint {
 | 
					export interface LenConstraint {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user