442 lines
10 KiB
Rust
442 lines
10 KiB
Rust
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<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,
|
|
}
|
|
|
|
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,
|
|
}
|
|
|
|
/// 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<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,
|
|
}
|