Add first routes for accounts management
This commit is contained in:
		
							
								
								
									
										35
									
								
								moneymgr_backend/src/controllers/accounts_controller.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								moneymgr_backend/src/controllers/accounts_controller.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
use crate::controllers::HttpResult;
 | 
			
		||||
use crate::controllers::server_controller::ServerConstraints;
 | 
			
		||||
use crate::extractors::auth_extractor::AuthExtractor;
 | 
			
		||||
use crate::services::accounts_service;
 | 
			
		||||
use crate::services::accounts_service::UpdateAccountQuery;
 | 
			
		||||
use actix_web::{HttpResponse, web};
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
pub struct CreateAccountRequest {
 | 
			
		||||
    name: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a new account
 | 
			
		||||
pub async fn create(auth: AuthExtractor, req: web::Json<CreateAccountRequest>) -> HttpResult {
 | 
			
		||||
    let constraints = ServerConstraints::default();
 | 
			
		||||
 | 
			
		||||
    if !constraints.account_name.check_str(&req.name) {
 | 
			
		||||
        return Ok(HttpResponse::BadRequest().json("Invalid account name length!"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    accounts_service::create(
 | 
			
		||||
        auth.user_id(),
 | 
			
		||||
        &UpdateAccountQuery {
 | 
			
		||||
            name: req.name.clone(),
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
    .await?;
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::Created().finish())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the list of accounts of the user
 | 
			
		||||
pub async fn get_list(auth: AuthExtractor) -> HttpResult {
 | 
			
		||||
    Ok(HttpResponse::Ok().json(accounts_service::get_list_user(auth.user_id()).await?))
 | 
			
		||||
}
 | 
			
		||||
@@ -2,6 +2,7 @@ use actix_web::http::StatusCode;
 | 
			
		||||
use actix_web::{HttpResponse, ResponseError};
 | 
			
		||||
use std::error::Error;
 | 
			
		||||
 | 
			
		||||
pub mod accounts_controller;
 | 
			
		||||
pub mod auth_controller;
 | 
			
		||||
pub mod server_controller;
 | 
			
		||||
pub mod static_controller;
 | 
			
		||||
 
 | 
			
		||||
@@ -39,6 +39,7 @@ pub struct ServerConstraints {
 | 
			
		||||
    pub token_name: LenConstraints,
 | 
			
		||||
    pub token_ip_net: LenConstraints,
 | 
			
		||||
    pub token_max_inactivity: LenConstraints,
 | 
			
		||||
    pub account_name: LenConstraints,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for ServerConstraints {
 | 
			
		||||
@@ -47,6 +48,7 @@ impl Default for ServerConstraints {
 | 
			
		||||
            token_name: LenConstraints::new(5, 255),
 | 
			
		||||
            token_ip_net: LenConstraints::max_only(44),
 | 
			
		||||
            token_max_inactivity: LenConstraints::new(3600, 3600 * 24 * 365),
 | 
			
		||||
            account_name: LenConstraints::not_empty(50),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -141,10 +141,10 @@ impl FromRequest for AuthExtractor {
 | 
			
		||||
 | 
			
		||||
                // Check for authorization
 | 
			
		||||
                let uri = req.uri().to_string();
 | 
			
		||||
                let authorized = (uri.starts_with("/api/account/") && token.right_account)
 | 
			
		||||
                    || (uri.starts_with("/api/movement/") && token.right_movement)
 | 
			
		||||
                let authorized = (uri.starts_with("/api/accounts/") && token.right_account)
 | 
			
		||||
                    || (uri.starts_with("/api/movements/") && token.right_movement)
 | 
			
		||||
                    || (uri.starts_with("/api/inbox/") && token.right_inbox)
 | 
			
		||||
                    || (uri.starts_with("/api/attachment/") && token.right_attachment)
 | 
			
		||||
                    || (uri.starts_with("/api/attachments/") && token.right_attachment)
 | 
			
		||||
                    || (uri.starts_with("/api/auth/") && token.right_auth);
 | 
			
		||||
 | 
			
		||||
                if !authorized {
 | 
			
		||||
 
 | 
			
		||||
@@ -100,6 +100,19 @@ async fn main() -> std::io::Result<()> {
 | 
			
		||||
                "/api/tokens/{id}",
 | 
			
		||||
                web::delete().to(tokens_controller::delete),
 | 
			
		||||
            )
 | 
			
		||||
            // Accounts controller
 | 
			
		||||
            .route("/api/accounts", web::post().to(accounts_controller::create))
 | 
			
		||||
            .route(
 | 
			
		||||
                "/api/accounts/list",
 | 
			
		||||
                web::get().to(accounts_controller::get_list),
 | 
			
		||||
            )
 | 
			
		||||
            // TODO : update account
 | 
			
		||||
            //TODO
 | 
			
		||||
            /*.route(
 | 
			
		||||
                "/api/accounts/{id}",
 | 
			
		||||
                web::delete().to(accounts_controller::delete),
 | 
			
		||||
            )*/
 | 
			
		||||
            // TODO : set as default
 | 
			
		||||
            // Static assets
 | 
			
		||||
            .route("/", web::get().to(static_controller::root_index))
 | 
			
		||||
            .route(
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								moneymgr_backend/src/models/accounts.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								moneymgr_backend/src/models/accounts.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
use crate::models::users::UserID;
 | 
			
		||||
use crate::schema::*;
 | 
			
		||||
use diesel::prelude::*;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
 | 
			
		||||
pub struct AccountID(pub i32);
 | 
			
		||||
 | 
			
		||||
#[derive(Queryable, Debug, Clone, serde::Serialize)]
 | 
			
		||||
pub struct Account {
 | 
			
		||||
    id: i32,
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    user_id: i32,
 | 
			
		||||
    pub time_create: i64,
 | 
			
		||||
    pub time_update: i64,
 | 
			
		||||
    pub default_account: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Account {
 | 
			
		||||
    pub fn id(&self) -> AccountID {
 | 
			
		||||
        AccountID(self.id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn user_id(&self) -> UserID {
 | 
			
		||||
        UserID(self.user_id)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Insertable)]
 | 
			
		||||
#[diesel(table_name = accounts)]
 | 
			
		||||
pub struct NewAccount<'a> {
 | 
			
		||||
    pub name: &'a str,
 | 
			
		||||
    pub user_id: i32,
 | 
			
		||||
    pub time_create: i64,
 | 
			
		||||
    pub time_update: i64,
 | 
			
		||||
}
 | 
			
		||||
@@ -1,2 +1,3 @@
 | 
			
		||||
pub mod accounts;
 | 
			
		||||
pub mod tokens;
 | 
			
		||||
pub mod users;
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,7 @@ impl Token {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Insertable)]
 | 
			
		||||
#[diesel(table_name = token)]
 | 
			
		||||
#[diesel(table_name = tokens)]
 | 
			
		||||
pub struct NewToken<'a> {
 | 
			
		||||
    pub name: &'a str,
 | 
			
		||||
    pub user_id: i32,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
// @generated automatically by Diesel CLI.
 | 
			
		||||
 | 
			
		||||
diesel::table! {
 | 
			
		||||
    account (id) {
 | 
			
		||||
    accounts (id) {
 | 
			
		||||
        id -> Int4,
 | 
			
		||||
        #[max_length = 50]
 | 
			
		||||
        name -> Varchar,
 | 
			
		||||
@@ -13,7 +13,7 @@ diesel::table! {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
diesel::table! {
 | 
			
		||||
    attachment (id) {
 | 
			
		||||
    attachments (id) {
 | 
			
		||||
        id -> Int4,
 | 
			
		||||
        time_create -> Int8,
 | 
			
		||||
        #[max_length = 150]
 | 
			
		||||
@@ -37,7 +37,7 @@ diesel::table! {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
diesel::table! {
 | 
			
		||||
    movement (id) {
 | 
			
		||||
    movements (id) {
 | 
			
		||||
        id -> Int4,
 | 
			
		||||
        account_id -> Int4,
 | 
			
		||||
        time -> Int8,
 | 
			
		||||
@@ -52,7 +52,7 @@ diesel::table! {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
diesel::table! {
 | 
			
		||||
    token (id) {
 | 
			
		||||
    tokens (id) {
 | 
			
		||||
        id -> Int4,
 | 
			
		||||
        #[max_length = 150]
 | 
			
		||||
        name -> Varchar,
 | 
			
		||||
@@ -85,13 +85,20 @@ diesel::table! {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
diesel::joinable!(account -> users (user_id));
 | 
			
		||||
diesel::joinable!(attachment -> users (user_id));
 | 
			
		||||
diesel::joinable!(inbox -> account (account_id));
 | 
			
		||||
diesel::joinable!(inbox -> attachment (attachment_id));
 | 
			
		||||
diesel::joinable!(accounts -> users (user_id));
 | 
			
		||||
diesel::joinable!(attachments -> users (user_id));
 | 
			
		||||
diesel::joinable!(inbox -> accounts (account_id));
 | 
			
		||||
diesel::joinable!(inbox -> attachments (attachment_id));
 | 
			
		||||
diesel::joinable!(inbox -> users (user_id));
 | 
			
		||||
diesel::joinable!(movement -> account (account_id));
 | 
			
		||||
diesel::joinable!(movement -> attachment (attachment_id));
 | 
			
		||||
diesel::joinable!(token -> users (user_id));
 | 
			
		||||
diesel::joinable!(movements -> accounts (account_id));
 | 
			
		||||
diesel::joinable!(movements -> attachments (attachment_id));
 | 
			
		||||
diesel::joinable!(tokens -> users (user_id));
 | 
			
		||||
 | 
			
		||||
diesel::allow_tables_to_appear_in_same_query!(account, attachment, inbox, movement, token, users,);
 | 
			
		||||
diesel::allow_tables_to_appear_in_same_query!(
 | 
			
		||||
    accounts,
 | 
			
		||||
    attachments,
 | 
			
		||||
    inbox,
 | 
			
		||||
    movements,
 | 
			
		||||
    tokens,
 | 
			
		||||
    users,
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										64
									
								
								moneymgr_backend/src/services/accounts_service.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								moneymgr_backend/src/services/accounts_service.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
use crate::connections::db_connection::db;
 | 
			
		||||
use crate::models::accounts::{Account, AccountID, NewAccount};
 | 
			
		||||
use crate::models::users::UserID;
 | 
			
		||||
use crate::schema::accounts;
 | 
			
		||||
use crate::utils::time_utils::time;
 | 
			
		||||
use diesel::RunQueryDsl;
 | 
			
		||||
use diesel::prelude::*;
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
pub struct UpdateAccountQuery {
 | 
			
		||||
    pub name: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a new account
 | 
			
		||||
pub async fn create(user_id: UserID, query: &UpdateAccountQuery) -> anyhow::Result<Account> {
 | 
			
		||||
    let new_account = NewAccount {
 | 
			
		||||
        name: query.name.as_str(),
 | 
			
		||||
        user_id: user_id.0,
 | 
			
		||||
        time_create: time() as i64,
 | 
			
		||||
        time_update: time() as i64,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let res: Account = diesel::insert_into(accounts::table)
 | 
			
		||||
        .values(&new_account)
 | 
			
		||||
        .get_result(&mut db()?)?;
 | 
			
		||||
 | 
			
		||||
    update(res.id(), query).await?;
 | 
			
		||||
 | 
			
		||||
    Ok(res)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Update an account
 | 
			
		||||
pub async fn update(id: AccountID, q: &UpdateAccountQuery) -> anyhow::Result<()> {
 | 
			
		||||
    diesel::update(accounts::dsl::accounts.filter(accounts::dsl::id.eq(id.0)))
 | 
			
		||||
        .set((
 | 
			
		||||
            accounts::dsl::time_update.eq(time() as i64),
 | 
			
		||||
            accounts::dsl::name.eq(&q.name),
 | 
			
		||||
        ))
 | 
			
		||||
        .execute(&mut db()?)?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get a single account by its id
 | 
			
		||||
pub async fn get_by_id(account_id: AccountID) -> anyhow::Result<Account> {
 | 
			
		||||
    Ok(accounts::table
 | 
			
		||||
        .filter(accounts::dsl::id.eq(account_id.0))
 | 
			
		||||
        .get_result(&mut db()?)?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the accounts of a user
 | 
			
		||||
pub async fn get_list_user(id: UserID) -> anyhow::Result<Vec<Account>> {
 | 
			
		||||
    Ok(accounts::table
 | 
			
		||||
        .filter(accounts::dsl::user_id.eq(id.0))
 | 
			
		||||
        .get_results(&mut db()?)?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Delete an account
 | 
			
		||||
pub async fn delete(id: AccountID) -> anyhow::Result<()> {
 | 
			
		||||
    diesel::delete(accounts::dsl::accounts.filter(accounts::dsl::id.eq(id.0)))
 | 
			
		||||
        .execute(&mut db()?)?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
@@ -1,2 +1,3 @@
 | 
			
		||||
pub mod accounts_service;
 | 
			
		||||
pub mod tokens_service;
 | 
			
		||||
pub mod users_service;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ use crate::connections::db_connection::db;
 | 
			
		||||
use crate::constants;
 | 
			
		||||
use crate::models::tokens::{NewToken, Token, TokenID};
 | 
			
		||||
use crate::models::users::UserID;
 | 
			
		||||
use crate::schema::token;
 | 
			
		||||
use crate::schema::tokens;
 | 
			
		||||
use crate::utils::rand_utils::rand_string;
 | 
			
		||||
use crate::utils::time_utils::time;
 | 
			
		||||
 | 
			
		||||
@@ -41,7 +41,7 @@ pub async fn create(new_token: NewTokenInfo) -> anyhow::Result<Token> {
 | 
			
		||||
        right_attachment: new_token.right_attachment,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let res = diesel::insert_into(token::table)
 | 
			
		||||
    let res = diesel::insert_into(tokens::table)
 | 
			
		||||
        .values(&t)
 | 
			
		||||
        .get_result(&mut db()?)?;
 | 
			
		||||
 | 
			
		||||
@@ -50,36 +50,36 @@ pub async fn create(new_token: NewTokenInfo) -> anyhow::Result<Token> {
 | 
			
		||||
 | 
			
		||||
/// Get a single token by its id
 | 
			
		||||
pub async fn get_by_id(token_id: TokenID) -> anyhow::Result<Token> {
 | 
			
		||||
    Ok(token::table
 | 
			
		||||
        .filter(token::dsl::id.eq(token_id.0))
 | 
			
		||||
    Ok(tokens::table
 | 
			
		||||
        .filter(tokens::dsl::id.eq(token_id.0))
 | 
			
		||||
        .get_result(&mut db()?)?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get a single token by its name
 | 
			
		||||
pub fn get_by_name(name: &str) -> anyhow::Result<Token> {
 | 
			
		||||
    Ok(token::table
 | 
			
		||||
        .filter(token::dsl::name.eq(name))
 | 
			
		||||
    Ok(tokens::table
 | 
			
		||||
        .filter(tokens::dsl::name.eq(name))
 | 
			
		||||
        .get_result(&mut db()?)?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get a single token by its value
 | 
			
		||||
pub async fn get_by_value(value: &str) -> anyhow::Result<Token> {
 | 
			
		||||
    Ok(token::table
 | 
			
		||||
        .filter(token::dsl::token_value.eq(value))
 | 
			
		||||
    Ok(tokens::table
 | 
			
		||||
        .filter(tokens::dsl::token_value.eq(value))
 | 
			
		||||
        .get_result(&mut db()?)?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the token of a user
 | 
			
		||||
pub async fn get_list_user(id: UserID) -> anyhow::Result<Vec<Token>> {
 | 
			
		||||
    Ok(token::table
 | 
			
		||||
        .filter(token::dsl::user_id.eq(id.0))
 | 
			
		||||
    Ok(tokens::table
 | 
			
		||||
        .filter(tokens::dsl::user_id.eq(id.0))
 | 
			
		||||
        .get_results(&mut db()?)?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Update last used value of a token
 | 
			
		||||
pub async fn update_time_used(token: &Token) -> anyhow::Result<()> {
 | 
			
		||||
    diesel::update(token::dsl::token.filter(token::dsl::id.eq(token.id().0)))
 | 
			
		||||
        .set(token::dsl::time_used.eq(time() as i64))
 | 
			
		||||
    diesel::update(tokens::dsl::tokens.filter(tokens::dsl::id.eq(token.id().0)))
 | 
			
		||||
        .set(tokens::dsl::time_used.eq(time() as i64))
 | 
			
		||||
        .execute(&mut db()?)?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
@@ -87,10 +87,10 @@ pub async fn update_time_used(token: &Token) -> anyhow::Result<()> {
 | 
			
		||||
/// Delete the token of a user
 | 
			
		||||
pub async fn delete(user_id: UserID, token_id: TokenID) -> anyhow::Result<()> {
 | 
			
		||||
    diesel::delete(
 | 
			
		||||
        token::dsl::token.filter(
 | 
			
		||||
            token::dsl::id
 | 
			
		||||
        tokens::dsl::tokens.filter(
 | 
			
		||||
            tokens::dsl::id
 | 
			
		||||
                .eq(token_id.0)
 | 
			
		||||
                .and(token::dsl::user_id.eq(user_id.0)),
 | 
			
		||||
                .and(tokens::dsl::user_id.eq(user_id.0)),
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
    .execute(&mut db()?)?;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user