Can create a family
This commit is contained in:
		@@ -14,18 +14,18 @@ CREATE TABLE users (
 | 
				
			|||||||
    admin BOOLEAN NOT NULL DEFAULT FALSE
 | 
					    admin BOOLEAN NOT NULL DEFAULT FALSE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE families(
 | 
					CREATE TABLE families (
 | 
				
			||||||
    id SERIAL PRIMARY KEY,
 | 
					    id SERIAL PRIMARY KEY,
 | 
				
			||||||
    time_create BIGINT NOT NULL,
 | 
					    time_create BIGINT NOT NULL,
 | 
				
			||||||
    name VARCHAR(30) NOT NULL,
 | 
					    name VARCHAR(30) NOT NULL,
 | 
				
			||||||
    invitation_code VARCHAR(7) NOT NULL
 | 
					    invitation_code VARCHAR(7) NOT NULL
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE memberships(
 | 
					CREATE TABLE memberships (
 | 
				
			||||||
    user_id integer NOT NULL REFERENCES users,
 | 
					    user_id integer NOT NULL REFERENCES users,
 | 
				
			||||||
    family_id integer NOT NULL REFERENCES families,
 | 
					    family_id integer NOT NULL REFERENCES families,
 | 
				
			||||||
    time_create BIGINT NOT NULL,
 | 
					    time_create BIGINT NOT NULL,
 | 
				
			||||||
    is_admin BOOLEAN NOT NULL DEFAULT TRUE,
 | 
					    is_admin BOOLEAN NOT NULL DEFAULT FALSE,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    PRIMARY KEY(user_id, family_id)
 | 
					    PRIMARY KEY(user_id, family_id)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -22,6 +22,7 @@ pub struct StaticConstraints {
 | 
				
			|||||||
    pub mail_len: SizeConstraint,
 | 
					    pub mail_len: SizeConstraint,
 | 
				
			||||||
    pub user_name_len: SizeConstraint,
 | 
					    pub user_name_len: SizeConstraint,
 | 
				
			||||||
    pub password_len: SizeConstraint,
 | 
					    pub password_len: SizeConstraint,
 | 
				
			||||||
 | 
					    pub family_name_len: SizeConstraint,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Default for StaticConstraints {
 | 
					impl Default for StaticConstraints {
 | 
				
			||||||
@@ -30,6 +31,7 @@ impl Default for StaticConstraints {
 | 
				
			|||||||
            mail_len: SizeConstraint::new(5, 255),
 | 
					            mail_len: SizeConstraint::new(5, 255),
 | 
				
			||||||
            user_name_len: SizeConstraint::new(3, 30),
 | 
					            user_name_len: SizeConstraint::new(3, 30),
 | 
				
			||||||
            password_len: SizeConstraint::new(8, 255),
 | 
					            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
 | 
					/// OpenID state duration
 | 
				
			||||||
pub const OPEN_ID_STATE_DURATION: Duration = Duration::from_secs(3600);
 | 
					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};
 | 
					use std::fmt::{Debug, Display, Formatter};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub mod auth_controller;
 | 
					pub mod auth_controller;
 | 
				
			||||||
 | 
					pub mod families_controller;
 | 
				
			||||||
pub mod server_controller;
 | 
					pub mod server_controller;
 | 
				
			||||||
pub mod user_controller;
 | 
					pub mod users_controller;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Custom error to ease controller writing
 | 
					/// Custom error to ease controller writing
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,9 @@ use actix_remote_ip::RemoteIPConfig;
 | 
				
			|||||||
use actix_web::middleware::Logger;
 | 
					use actix_web::middleware::Logger;
 | 
				
			||||||
use actix_web::{web, App, HttpServer};
 | 
					use actix_web::{web, App, HttpServer};
 | 
				
			||||||
use geneit_backend::app_config::AppConfig;
 | 
					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]
 | 
					#[actix_web::main]
 | 
				
			||||||
async fn main() -> std::io::Result<()> {
 | 
					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))
 | 
					            .route("/auth/logout", web::get().to(auth_controller::logout))
 | 
				
			||||||
            // User controller
 | 
					            // User controller
 | 
				
			||||||
            .route("/user/info", web::get().to(user_controller::auth_info))
 | 
					            .route("/user/info", web::get().to(users_controller::auth_info))
 | 
				
			||||||
            .route(
 | 
					            .route(
 | 
				
			||||||
                "/user/update_profile",
 | 
					                "/user/update_profile",
 | 
				
			||||||
                web::post().to(user_controller::update_profile),
 | 
					                web::post().to(users_controller::update_profile),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .route(
 | 
					            .route(
 | 
				
			||||||
                "/user/replace_password",
 | 
					                "/user/replace_password",
 | 
				
			||||||
                web::post().to(user_controller::replace_password),
 | 
					                web::post().to(users_controller::replace_password),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .route(
 | 
					            .route(
 | 
				
			||||||
                "/user/request_delete",
 | 
					                "/user/request_delete",
 | 
				
			||||||
                web::get().to(user_controller::request_delete_account),
 | 
					                web::get().to(users_controller::request_delete_account),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .route(
 | 
					            .route(
 | 
				
			||||||
                "/user/check_delete_token",
 | 
					                "/user/check_delete_token",
 | 
				
			||||||
                web::post().to(user_controller::check_delete_token),
 | 
					                web::post().to(users_controller::check_delete_token),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .route(
 | 
					            .route(
 | 
				
			||||||
                "/user/delete_account",
 | 
					                "/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())?
 | 
					    .bind(AppConfig::get().listen_address.as_str())?
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
use crate::schema::users;
 | 
					use crate::schema::{families, memberships, users};
 | 
				
			||||||
use diesel::prelude::*;
 | 
					use diesel::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// User ID holder
 | 
					/// User ID holder
 | 
				
			||||||
@@ -51,3 +51,46 @@ pub struct NewUser<'a> {
 | 
				
			|||||||
    pub email: &'a str,
 | 
					    pub email: &'a str,
 | 
				
			||||||
    pub time_create: i64,
 | 
					    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 -> families (family_id));
 | 
				
			||||||
diesel::joinable!(memberships -> users (user_id));
 | 
					diesel::joinable!(memberships -> users (user_id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
diesel::allow_tables_to_appear_in_same_query!(
 | 
					diesel::allow_tables_to_appear_in_same_query!(families, memberships, users,);
 | 
				
			||||||
    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
 | 
					//! # Backend services
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub mod families_service;
 | 
				
			||||||
pub mod login_token_service;
 | 
					pub mod login_token_service;
 | 
				
			||||||
pub mod mail_service;
 | 
					pub mod mail_service;
 | 
				
			||||||
pub mod openid_service;
 | 
					pub mod openid_service;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user