use crate::app_config::AppConfig; use crate::schema::{couples, families, members, memberships, photos, users}; use crate::utils::crypt_utils::sha256; use diesel::prelude::*; /// User ID holder #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq)] pub struct UserID(pub i32); #[derive(Queryable, Debug, serde::Serialize)] pub struct User { id: i32, pub name: String, pub email: String, #[serde(skip_serializing)] pub password: Option, pub time_create: i64, #[serde(skip_serializing)] pub reset_password_token: Option, #[serde(skip_serializing)] pub time_gen_reset_token: i64, #[serde(skip_serializing)] pub delete_account_token: Option, #[serde(skip_serializing)] pub time_gen_delete_account_token: i64, pub time_activate: i64, pub active: bool, pub admin: bool, } impl User { pub fn id(&self) -> UserID { UserID(self.id) } pub fn check_password(&self, password: &str) -> bool { self.password .as_deref() .map(|hash| { bcrypt::verify(password, hash).unwrap_or_else(|e| { log::error!("Failed to validate password! {}", e); false }) }) .unwrap_or(false) } } #[derive(Insertable)] #[diesel(table_name = users)] pub struct NewUser<'a> { pub name: &'a str, pub email: &'a str, pub time_create: i64, } /// Family ID holder #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq)] pub struct FamilyID(pub i32); #[derive(Queryable, Debug, serde::Serialize)] pub struct Family { id: i32, pub time_create: i64, pub name: String, pub invitation_code: String, pub disable_couple_photos: bool, pub enable_genealogy: bool, } 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, pub time_create: i64, pub is_admin: bool, } impl Membership { pub fn user_id(&self) -> UserID { UserID(self.user_id) } pub fn family_id(&self) -> FamilyID { FamilyID(self.family_id) } } #[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, } #[derive(Queryable, Debug, serde::Serialize)] pub struct FamilyMembership { user_id: i32, family_id: i32, name: String, time_create: i64, pub is_admin: bool, invitation_code: String, pub count_members: i64, pub count_admins: i64, } /// Photo ID holder #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)] pub struct PhotoID(pub i32); impl PhotoID { pub fn to_signed_hash(&self) -> String { let secret = AppConfig::get().secret(); format!( "{}-{}", self.0, sha256(format!("{secret}{}{secret}", self.0).as_bytes()) ) } pub fn from_signed_hash(hash: &str) -> Option { let (id, _) = hash.split_once('-')?; let id = Self(id.parse().ok()?); if id.to_signed_hash() != hash { return None; } Some(id) } } #[derive(Queryable, Debug, serde::Serialize)] pub struct Photo { id: i32, pub file_id: String, pub time_create: i64, pub mime_type: String, pub sha512: String, pub file_size: i32, pub thumb_sha512: String, } impl Photo { pub fn id(&self) -> PhotoID { PhotoID(self.id) } pub fn photo_path(&self) -> String { format!("photo/{}", self.file_id) } pub fn thumbnail_path(&self) -> String { format!("thumbnail/{}", self.file_id) } pub fn mime_extension(&self) -> Option<&str> { mime_guess::get_mime_extensions_str(&self.mime_type) .map(|e| e.first()) .unwrap_or_default() .copied() } } #[derive(Insertable)] #[diesel(table_name = photos)] pub struct NewPhoto { pub file_id: String, pub time_create: i64, pub mime_type: String, pub sha512: String, pub file_size: i32, pub thumb_sha512: String, } impl NewPhoto { pub fn photo_path(&self) -> String { format!("photo/{}", self.file_id) } pub fn thumbnail_path(&self) -> String { format!("thumbnail/{}", self.file_id) } } /// Member ID holder #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)] pub struct MemberID(pub i32); #[derive(serde::Serialize, serde::Deserialize, Copy, Clone)] pub enum Sex { #[serde(rename = "M")] Male, #[serde(rename = "F")] Female, } impl Sex { pub fn parse_str(s: &str) -> Option { match s { "M" => Some(Sex::Male), "F" => Some(Sex::Female), _ => None, } } pub fn to_str(&self) -> &'static str { match self { Sex::Male => "M", Sex::Female => "F", } } } #[derive(Queryable, Debug, serde::Serialize, Clone)] pub struct Member { id: i32, family_id: i32, pub first_name: Option, pub last_name: Option, pub birth_last_name: Option, photo_id: Option, pub email: Option, pub phone: Option, pub address: Option, pub city: Option, pub postal_code: Option, pub country: Option, sex: Option, time_create: i64, pub time_update: i64, mother: Option, father: Option, pub birth_year: Option, pub birth_month: Option, pub birth_day: Option, pub dead: bool, pub death_year: Option, pub death_month: Option, pub death_day: Option, pub note: Option, } impl Member { pub fn id(&self) -> MemberID { MemberID(self.id) } pub fn family_id(&self) -> FamilyID { FamilyID(self.family_id) } pub fn set_photo_id(&mut self, p: Option) { self.photo_id = p.map(|p| p.0) } pub fn photo_id(&self) -> Option { self.photo_id.map(PhotoID) } pub fn sex(&self) -> Option { self.sex.as_deref().map(Sex::parse_str).unwrap_or_default() } pub fn set_sex(&mut self, s: Option) { self.sex = s.map(|s| s.to_str().to_string()) } pub fn mother(&self) -> Option { self.mother.map(MemberID) } pub fn set_mother(&mut self, p: Option) { self.mother = p.map(|p| p.0); } pub fn father(&self) -> Option { self.father.map(MemberID) } pub fn set_father(&mut self, p: Option) { self.father = p.map(|p| p.0); } } #[derive(Insertable)] #[diesel(table_name = members)] pub struct NewMember { pub family_id: i32, pub time_create: i64, pub time_update: i64, } /// Member ID holder #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)] pub struct CoupleID(pub i32); #[derive(serde::Serialize, serde::Deserialize, Copy, Clone)] pub enum CoupleState { #[serde(rename = "N")] None, #[serde(rename = "E")] Engaged, #[serde(rename = "M")] Married, #[serde(rename = "D")] Divorced, } #[derive(Debug, Clone, serde::Serialize)] pub struct CoupleStateDesc { code: &'static str, fr: &'static str, en: &'static str, } impl CoupleState { pub fn as_str(&self) -> &str { match self { CoupleState::None => "N", CoupleState::Engaged => "E", CoupleState::Married => "M", CoupleState::Divorced => "D", } } pub fn parse_str(s: &str) -> Option { serde_json::from_str(&format!("\"{s}\"")).ok() } pub fn states_list() -> Vec { vec![ CoupleStateDesc { code: "N", fr: "Aucun", en: "None", }, CoupleStateDesc { code: "E", fr: "Fiancés", en: "Engaged", }, CoupleStateDesc { code: "M", fr: "Mariés", en: "Married", }, CoupleStateDesc { code: "D", fr: "Divorcés", en: "Divorced", }, ] } } #[derive(Queryable, Debug, serde::Serialize)] pub struct Couple { id: i32, family_id: i32, wife: Option, husband: Option, state: Option, photo_id: Option, time_create: i64, pub time_update: i64, pub wedding_year: Option, pub wedding_month: Option, pub wedding_day: Option, pub divorce_year: Option, pub divorce_month: Option, pub divorce_day: Option, } impl Couple { pub fn id(&self) -> CoupleID { CoupleID(self.id) } pub fn family_id(&self) -> FamilyID { FamilyID(self.family_id) } pub fn state(&self) -> Option { self.state .as_deref() .map(CoupleState::parse_str) .unwrap_or_default() } pub fn set_state(&mut self, s: Option) { self.state = s.map(|s| s.as_str().to_string()) } pub fn set_wife(&mut self, s: Option) { self.wife = s.map(|s| s.0) } pub fn wife(&self) -> Option { self.wife.map(MemberID) } pub fn set_husband(&mut self, s: Option) { self.husband = s.map(|s| s.0) } pub fn husband(&self) -> Option { self.husband.map(MemberID) } pub fn set_photo_id(&mut self, p: Option) { self.photo_id = p.map(|p| p.0); } pub fn photo_id(&self) -> Option { self.photo_id.map(PhotoID) } } #[derive(Insertable)] #[diesel(table_name = couples)] pub struct NewCouple { pub family_id: i32, pub time_create: i64, pub time_update: i64, }