All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			
		
			
				
	
	
		
			600 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			600 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use crate::app_config::AppConfig;
 | |
| use crate::constants;
 | |
| use crate::schema::{
 | |
|     accommodations_list, accommodations_reservations, accommodations_reservations_cals_urls,
 | |
|     couples, families, members, memberships, photos, users,
 | |
| };
 | |
| use crate::utils::crypt_utils::sha256;
 | |
| use crate::utils::time_utils::time;
 | |
| 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<String>,
 | |
|     pub time_create: i64,
 | |
|     #[serde(skip_serializing)]
 | |
|     pub reset_password_token: Option<String>,
 | |
|     #[serde(skip_serializing)]
 | |
|     pub time_gen_reset_token: i64,
 | |
|     #[serde(skip_serializing)]
 | |
|     pub delete_account_token: Option<String>,
 | |
|     #[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,
 | |
|     pub enable_accommodations: 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<Self> {
 | |
|         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<Self> {
 | |
|         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<String>,
 | |
|     pub last_name: Option<String>,
 | |
|     pub birth_last_name: Option<String>,
 | |
|     photo_id: Option<i32>,
 | |
|     pub email: Option<String>,
 | |
|     pub phone: Option<String>,
 | |
|     pub address: Option<String>,
 | |
|     pub city: Option<String>,
 | |
|     pub postal_code: Option<String>,
 | |
|     pub country: Option<String>,
 | |
|     sex: Option<String>,
 | |
|     time_create: i64,
 | |
|     pub time_update: i64,
 | |
|     mother: Option<i32>,
 | |
|     father: Option<i32>,
 | |
|     pub birth_year: Option<i16>,
 | |
|     pub birth_month: Option<i16>,
 | |
|     pub birth_day: Option<i16>,
 | |
|     pub dead: bool,
 | |
|     pub death_year: Option<i16>,
 | |
|     pub death_month: Option<i16>,
 | |
|     pub death_day: Option<i16>,
 | |
|     pub note: Option<String>,
 | |
| }
 | |
| 
 | |
| 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<PhotoID>) {
 | |
|         self.photo_id = p.map(|p| p.0)
 | |
|     }
 | |
| 
 | |
|     pub fn photo_id(&self) -> Option<PhotoID> {
 | |
|         self.photo_id.map(PhotoID)
 | |
|     }
 | |
| 
 | |
|     pub fn sex(&self) -> Option<Sex> {
 | |
|         self.sex.as_deref().map(Sex::parse_str).unwrap_or_default()
 | |
|     }
 | |
| 
 | |
|     pub fn set_sex(&mut self, s: Option<Sex>) {
 | |
|         self.sex = s.map(|s| s.to_str().to_string())
 | |
|     }
 | |
| 
 | |
|     pub fn mother(&self) -> Option<MemberID> {
 | |
|         self.mother.map(MemberID)
 | |
|     }
 | |
| 
 | |
|     pub fn set_mother(&mut self, p: Option<MemberID>) {
 | |
|         self.mother = p.map(|p| p.0);
 | |
|     }
 | |
| 
 | |
|     pub fn father(&self) -> Option<MemberID> {
 | |
|         self.father.map(MemberID)
 | |
|     }
 | |
| 
 | |
|     pub fn set_father(&mut self, p: Option<MemberID>) {
 | |
|         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,
 | |
| }
 | |
| 
 | |
| /// Couple 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<Self> {
 | |
|         serde_json::from_str(&format!("\"{s}\"")).ok()
 | |
|     }
 | |
| 
 | |
|     pub fn states_list() -> Vec<CoupleStateDesc> {
 | |
|         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<i32>,
 | |
|     husband: Option<i32>,
 | |
|     state: Option<String>,
 | |
|     photo_id: Option<i32>,
 | |
|     time_create: i64,
 | |
|     pub time_update: i64,
 | |
|     pub wedding_year: Option<i16>,
 | |
|     pub wedding_month: Option<i16>,
 | |
|     pub wedding_day: Option<i16>,
 | |
|     pub divorce_year: Option<i16>,
 | |
|     pub divorce_month: Option<i16>,
 | |
|     pub divorce_day: Option<i16>,
 | |
| }
 | |
| 
 | |
| 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<CoupleState> {
 | |
|         self.state
 | |
|             .as_deref()
 | |
|             .map(CoupleState::parse_str)
 | |
|             .unwrap_or_default()
 | |
|     }
 | |
| 
 | |
|     pub fn set_state(&mut self, s: Option<CoupleState>) {
 | |
|         self.state = s.map(|s| s.as_str().to_string())
 | |
|     }
 | |
| 
 | |
|     pub fn set_wife(&mut self, s: Option<MemberID>) {
 | |
|         self.wife = s.map(|s| s.0)
 | |
|     }
 | |
| 
 | |
|     pub fn wife(&self) -> Option<MemberID> {
 | |
|         self.wife.map(MemberID)
 | |
|     }
 | |
| 
 | |
|     pub fn set_husband(&mut self, s: Option<MemberID>) {
 | |
|         self.husband = s.map(|s| s.0)
 | |
|     }
 | |
| 
 | |
|     pub fn husband(&self) -> Option<MemberID> {
 | |
|         self.husband.map(MemberID)
 | |
|     }
 | |
| 
 | |
|     pub fn set_photo_id(&mut self, p: Option<PhotoID>) {
 | |
|         self.photo_id = p.map(|p| p.0);
 | |
|     }
 | |
| 
 | |
|     pub fn photo_id(&self) -> Option<PhotoID> {
 | |
|         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,
 | |
| }
 | |
| 
 | |
| /// Accommodation ID holder
 | |
| #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
 | |
| pub struct AccommodationID(pub i32);
 | |
| 
 | |
| #[derive(Queryable, Debug, serde::Serialize)]
 | |
| pub struct Accommodation {
 | |
|     id: i32,
 | |
|     family_id: i32,
 | |
|     time_create: i64,
 | |
|     pub time_update: i64,
 | |
|     pub name: String,
 | |
|     pub need_validation: bool,
 | |
|     pub description: Option<String>,
 | |
|     pub color: Option<String>,
 | |
|     pub open_to_reservations: bool,
 | |
| }
 | |
| 
 | |
| impl Accommodation {
 | |
|     pub fn id(&self) -> AccommodationID {
 | |
|         AccommodationID(self.id)
 | |
|     }
 | |
| 
 | |
|     pub fn family_id(&self) -> FamilyID {
 | |
|         FamilyID(self.family_id)
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[derive(Insertable)]
 | |
| #[diesel(table_name = accommodations_list)]
 | |
| pub struct NewAccommodation {
 | |
|     pub family_id: i32,
 | |
|     pub name: String,
 | |
|     pub time_create: i64,
 | |
|     pub time_update: i64,
 | |
| }
 | |
| 
 | |
| /// Accommodation reservation ID holder
 | |
| #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
 | |
| pub struct AccommodationReservationID(pub i32);
 | |
| 
 | |
| pub enum ReservationStatus {
 | |
|     Pending,
 | |
|     Accepted,
 | |
|     Rejected,
 | |
| }
 | |
| 
 | |
| #[derive(Queryable, Debug, serde::Serialize)]
 | |
| pub struct AccommodationReservation {
 | |
|     id: i32,
 | |
|     family_id: i32,
 | |
|     accommodation_id: i32,
 | |
|     user_id: i32,
 | |
|     time_create: i64,
 | |
|     pub time_update: i64,
 | |
|     pub reservation_start: i64,
 | |
|     pub reservation_end: i64,
 | |
|     pub validated: Option<bool>,
 | |
| }
 | |
| 
 | |
| impl AccommodationReservation {
 | |
|     pub fn id(&self) -> AccommodationReservationID {
 | |
|         AccommodationReservationID(self.id)
 | |
|     }
 | |
| 
 | |
|     pub fn accommodation_id(&self) -> AccommodationID {
 | |
|         AccommodationID(self.accommodation_id)
 | |
|     }
 | |
| 
 | |
|     pub fn family_id(&self) -> FamilyID {
 | |
|         FamilyID(self.family_id)
 | |
|     }
 | |
| 
 | |
|     pub fn user_id(&self) -> UserID {
 | |
|         UserID(self.user_id)
 | |
|     }
 | |
| 
 | |
|     pub fn status(&self) -> ReservationStatus {
 | |
|         match self.validated {
 | |
|             None => ReservationStatus::Pending,
 | |
|             Some(true) => ReservationStatus::Accepted,
 | |
|             Some(false) => ReservationStatus::Rejected,
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[derive(Insertable)]
 | |
| #[diesel(table_name = accommodations_reservations)]
 | |
| pub struct NewAccommodationReservation {
 | |
|     pub family_id: i32,
 | |
|     pub accommodation_id: i32,
 | |
|     pub user_id: i32,
 | |
|     pub time_create: i64,
 | |
|     pub time_update: i64,
 | |
|     pub reservation_start: i64,
 | |
|     pub reservation_end: i64,
 | |
| }
 | |
| 
 | |
| /// Accommodation reservation calendar ID holder
 | |
| #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
 | |
| pub struct AccommodationReservationCalendarID(pub i32);
 | |
| 
 | |
| #[derive(Queryable, Debug, serde::Serialize)]
 | |
| pub struct AccommodationReservationCalendar {
 | |
|     id: i32,
 | |
|     family_id: i32,
 | |
|     accommodation_id: Option<i32>,
 | |
|     user_id: i32,
 | |
|     name: String,
 | |
|     token: String,
 | |
|     pub time_create: i64,
 | |
|     pub time_used: i64,
 | |
| }
 | |
| 
 | |
| impl AccommodationReservationCalendar {
 | |
|     pub fn id(&self) -> AccommodationReservationCalendarID {
 | |
|         AccommodationReservationCalendarID(self.id)
 | |
|     }
 | |
| 
 | |
|     pub fn accommodation_id(&self) -> Option<AccommodationID> {
 | |
|         self.accommodation_id.map(AccommodationID)
 | |
|     }
 | |
| 
 | |
|     pub fn family_id(&self) -> FamilyID {
 | |
|         FamilyID(self.family_id)
 | |
|     }
 | |
| 
 | |
|     pub fn user_id(&self) -> UserID {
 | |
|         UserID(self.user_id)
 | |
|     }
 | |
| 
 | |
|     pub fn should_update_last_used(&self) -> bool {
 | |
|         (self.time_used
 | |
|             + constants::ACCOMMODATIONS_RESERVATIONS_CAL_URL_TIME_USED_UPDATE_MIN_INTERVAL.as_secs()
 | |
|                 as i64)
 | |
|             < time() as i64
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[derive(Insertable)]
 | |
| #[diesel(table_name = accommodations_reservations_cals_urls)]
 | |
| pub struct NewAccommodationReservationCalendar {
 | |
|     pub family_id: i32,
 | |
|     pub accommodation_id: Option<i32>,
 | |
|     pub user_id: i32,
 | |
|     pub name: String,
 | |
|     pub token: String,
 | |
|     pub time_create: i64,
 | |
|     pub time_used: i64,
 | |
| }
 |