Can create a family
This commit is contained in:
		@@ -22,6 +22,7 @@ pub struct StaticConstraints {
 | 
			
		||||
    pub mail_len: SizeConstraint,
 | 
			
		||||
    pub user_name_len: SizeConstraint,
 | 
			
		||||
    pub password_len: SizeConstraint,
 | 
			
		||||
    pub family_name_len: SizeConstraint,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for StaticConstraints {
 | 
			
		||||
@@ -30,6 +31,7 @@ impl Default for StaticConstraints {
 | 
			
		||||
            mail_len: SizeConstraint::new(5, 255),
 | 
			
		||||
            user_name_len: SizeConstraint::new(3, 30),
 | 
			
		||||
            password_len: SizeConstraint::new(8, 255),
 | 
			
		||||
            family_name_len: SizeConstraint::new(3, 30),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -42,3 +44,6 @@ pub const ACCOUNT_DELETE_TOKEN_DURATION: Duration = Duration::from_secs(3600 * 1
 | 
			
		||||
 | 
			
		||||
/// OpenID state duration
 | 
			
		||||
pub const OPEN_ID_STATE_DURATION: Duration = Duration::from_secs(3600);
 | 
			
		||||
 | 
			
		||||
/// Length of family invitation code
 | 
			
		||||
pub const FAMILY_INVITATION_CODE_LEN: usize = 7;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								geneit_backend/src/controllers/families_controller.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								geneit_backend/src/controllers/families_controller.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
use crate::constants::StaticConstraints;
 | 
			
		||||
use crate::controllers::HttpResult;
 | 
			
		||||
use crate::services::families_service;
 | 
			
		||||
use crate::services::login_token_service::LoginToken;
 | 
			
		||||
use actix_web::{web, HttpResponse};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, serde::Deserialize)]
 | 
			
		||||
pub struct CreateFamilyReq {
 | 
			
		||||
    name: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a new family
 | 
			
		||||
pub async fn create(req: web::Json<CreateFamilyReq>, token: LoginToken) -> HttpResult {
 | 
			
		||||
    if !StaticConstraints::default()
 | 
			
		||||
        .family_name_len
 | 
			
		||||
        .validate(&req.name)
 | 
			
		||||
    {
 | 
			
		||||
        return Ok(HttpResponse::BadRequest().body("Invalid family name!"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let family = families_service::create(&req.name, token.user_id).await?;
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::Created().json(family))
 | 
			
		||||
}
 | 
			
		||||
@@ -5,8 +5,9 @@ use actix_web::HttpResponse;
 | 
			
		||||
use std::fmt::{Debug, Display, Formatter};
 | 
			
		||||
 | 
			
		||||
pub mod auth_controller;
 | 
			
		||||
pub mod families_controller;
 | 
			
		||||
pub mod server_controller;
 | 
			
		||||
pub mod user_controller;
 | 
			
		||||
pub mod users_controller;
 | 
			
		||||
 | 
			
		||||
/// Custom error to ease controller writing
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,9 @@ use actix_remote_ip::RemoteIPConfig;
 | 
			
		||||
use actix_web::middleware::Logger;
 | 
			
		||||
use actix_web::{web, App, HttpServer};
 | 
			
		||||
use geneit_backend::app_config::AppConfig;
 | 
			
		||||
use geneit_backend::controllers::{auth_controller, server_controller, user_controller};
 | 
			
		||||
use geneit_backend::controllers::{
 | 
			
		||||
    auth_controller, families_controller, server_controller, users_controller,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[actix_web::main]
 | 
			
		||||
async fn main() -> std::io::Result<()> {
 | 
			
		||||
@@ -63,26 +65,31 @@ async fn main() -> std::io::Result<()> {
 | 
			
		||||
            )
 | 
			
		||||
            .route("/auth/logout", web::get().to(auth_controller::logout))
 | 
			
		||||
            // User controller
 | 
			
		||||
            .route("/user/info", web::get().to(user_controller::auth_info))
 | 
			
		||||
            .route("/user/info", web::get().to(users_controller::auth_info))
 | 
			
		||||
            .route(
 | 
			
		||||
                "/user/update_profile",
 | 
			
		||||
                web::post().to(user_controller::update_profile),
 | 
			
		||||
                web::post().to(users_controller::update_profile),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                "/user/replace_password",
 | 
			
		||||
                web::post().to(user_controller::replace_password),
 | 
			
		||||
                web::post().to(users_controller::replace_password),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                "/user/request_delete",
 | 
			
		||||
                web::get().to(user_controller::request_delete_account),
 | 
			
		||||
                web::get().to(users_controller::request_delete_account),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                "/user/check_delete_token",
 | 
			
		||||
                web::post().to(user_controller::check_delete_token),
 | 
			
		||||
                web::post().to(users_controller::check_delete_token),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                "/user/delete_account",
 | 
			
		||||
                web::post().to(user_controller::delete_account),
 | 
			
		||||
                web::post().to(users_controller::delete_account),
 | 
			
		||||
            )
 | 
			
		||||
            // Families controller
 | 
			
		||||
            .route(
 | 
			
		||||
                "/family/create",
 | 
			
		||||
                web::post().to(families_controller::create),
 | 
			
		||||
            )
 | 
			
		||||
    })
 | 
			
		||||
    .bind(AppConfig::get().listen_address.as_str())?
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use crate::schema::users;
 | 
			
		||||
use crate::schema::{families, memberships, users};
 | 
			
		||||
use diesel::prelude::*;
 | 
			
		||||
 | 
			
		||||
/// User ID holder
 | 
			
		||||
@@ -51,3 +51,46 @@ pub struct NewUser<'a> {
 | 
			
		||||
    pub email: &'a str,
 | 
			
		||||
    pub time_create: i64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Family ID holder
 | 
			
		||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
 | 
			
		||||
pub struct FamilyID(pub i32);
 | 
			
		||||
 | 
			
		||||
#[derive(Queryable, Debug, serde::Serialize)]
 | 
			
		||||
pub struct Family {
 | 
			
		||||
    pub id: i32,
 | 
			
		||||
    pub time_create: i64,
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub invitation_code: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Family {
 | 
			
		||||
    pub fn id(&self) -> FamilyID {
 | 
			
		||||
        FamilyID(self.id)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Insertable)]
 | 
			
		||||
#[diesel(table_name = families)]
 | 
			
		||||
pub struct NewFamily<'a> {
 | 
			
		||||
    pub name: &'a str,
 | 
			
		||||
    pub invitation_code: String,
 | 
			
		||||
    pub time_create: i64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Queryable, Debug, serde::Serialize)]
 | 
			
		||||
pub struct Membership {
 | 
			
		||||
    user_id: i32,
 | 
			
		||||
    family_id: i32,
 | 
			
		||||
    time_create: i64,
 | 
			
		||||
    is_admin: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Insertable)]
 | 
			
		||||
#[diesel(table_name = memberships)]
 | 
			
		||||
pub struct NewMembership {
 | 
			
		||||
    pub user_id: i32,
 | 
			
		||||
    pub family_id: i32,
 | 
			
		||||
    pub time_create: i64,
 | 
			
		||||
    pub is_admin: bool,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -38,8 +38,4 @@ diesel::table! {
 | 
			
		||||
diesel::joinable!(memberships -> families (family_id));
 | 
			
		||||
diesel::joinable!(memberships -> users (user_id));
 | 
			
		||||
 | 
			
		||||
diesel::allow_tables_to_appear_in_same_query!(
 | 
			
		||||
    families,
 | 
			
		||||
    memberships,
 | 
			
		||||
    users,
 | 
			
		||||
);
 | 
			
		||||
diesel::allow_tables_to_appear_in_same_query!(families, memberships, users,);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								geneit_backend/src/services/families_service.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								geneit_backend/src/services/families_service.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
use crate::connections::db_connection;
 | 
			
		||||
use crate::constants::FAMILY_INVITATION_CODE_LEN;
 | 
			
		||||
use crate::models::{Family, FamilyID, Membership, NewFamily, NewMembership, UserID};
 | 
			
		||||
use crate::schema::{families, memberships};
 | 
			
		||||
use crate::utils::string_utils::rand_str;
 | 
			
		||||
use crate::utils::time_utils::time;
 | 
			
		||||
use diesel::RunQueryDsl;
 | 
			
		||||
 | 
			
		||||
/// Create a new family, with an initial administrator
 | 
			
		||||
pub async fn create(name: &str, user_id: UserID) -> anyhow::Result<Family> {
 | 
			
		||||
    let family = db_connection::execute(|conn| {
 | 
			
		||||
        let res: Family = diesel::insert_into(families::table)
 | 
			
		||||
            .values(&NewFamily {
 | 
			
		||||
                name: name.trim(),
 | 
			
		||||
                invitation_code: rand_str(FAMILY_INVITATION_CODE_LEN),
 | 
			
		||||
                time_create: time() as i64,
 | 
			
		||||
            })
 | 
			
		||||
            .get_result(conn)?;
 | 
			
		||||
 | 
			
		||||
        Ok(res)
 | 
			
		||||
    })?;
 | 
			
		||||
 | 
			
		||||
    add_member(family.id(), user_id, true).await?;
 | 
			
		||||
 | 
			
		||||
    Ok(family)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Add a member to a family
 | 
			
		||||
pub async fn add_member(
 | 
			
		||||
    family_id: FamilyID,
 | 
			
		||||
    user_id: UserID,
 | 
			
		||||
    admin: bool,
 | 
			
		||||
) -> anyhow::Result<Membership> {
 | 
			
		||||
    db_connection::execute(|conn| {
 | 
			
		||||
        let res = diesel::insert_into(memberships::table)
 | 
			
		||||
            .values(&NewMembership {
 | 
			
		||||
                user_id: user_id.0,
 | 
			
		||||
                family_id: family_id.0,
 | 
			
		||||
                time_create: time() as i64,
 | 
			
		||||
                is_admin: admin,
 | 
			
		||||
            })
 | 
			
		||||
            .get_result(conn)?;
 | 
			
		||||
 | 
			
		||||
        Ok(res)
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
//! # Backend services
 | 
			
		||||
 | 
			
		||||
pub mod families_service;
 | 
			
		||||
pub mod login_token_service;
 | 
			
		||||
pub mod mail_service;
 | 
			
		||||
pub mod openid_service;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user