Phase 2.2: MongoDB connection and models

This commit is contained in:
goose 2026-02-14 15:37:02 -03:00
parent 1cf927f527
commit 154c3d1152
13 changed files with 544 additions and 5 deletions

View file

@ -0,0 +1,31 @@
use serde::{Deserialize, Serialize};
use mongodb::bson::{oid::ObjectId, DateTime};
use super::health_data::EncryptedField;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Appointment {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<ObjectId>,
#[serde(rename = "appointmentId")]
pub appointment_id: String,
#[serde(rename = "userId")]
pub user_id: String,
#[serde(rename = "profileId")]
pub profile_id: String,
#[serde(rename = "appointmentData")]
pub appointment_data: EncryptedField,
#[serde(rename = "reminders")]
pub reminders: Vec<AppointmentReminder>,
#[serde(rename = "createdAt")]
pub created_at: DateTime,
#[serde(rename = "updatedAt")]
pub updated_at: DateTime,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppointmentReminder {
#[serde(rename = "reminderId")]
pub reminder_id: String,
#[serde(rename = "scheduledTime")]
pub scheduled_time: String,
}

View file

@ -0,0 +1,43 @@
use serde::{Deserialize, Serialize};
use mongodb::{bson::{doc, oid::ObjectId, DateTime}, Collection};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Family {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<ObjectId>,
#[serde(rename = "familyId")]
pub family_id: String,
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "nameIv")]
pub name_iv: String,
#[serde(rename = "nameAuthTag")]
pub name_auth_tag: String,
#[serde(rename = "memberIds")]
pub member_ids: Vec<String>,
#[serde(rename = "createdAt")]
pub created_at: DateTime,
#[serde(rename = "updatedAt")]
pub updated_at: DateTime,
}
pub struct FamilyRepository {
collection: Collection<Family>,
}
impl FamilyRepository {
pub fn new(collection: Collection<Family>) -> Self {
Self { collection }
}
pub async fn create(&self, family: &Family) -> mongodb::error::Result<()> {
self.collection.insert_one(family, None).await?;
Ok(())
}
pub async fn find_by_family_id(&self, family_id: &str) -> mongodb::error::Result<Option<Family>> {
self.collection
.find_one(doc! { "familyId": family_id }, None)
.await
}
}

View file

@ -0,0 +1,32 @@
use serde::{Deserialize, Serialize};
use mongodb::bson::{oid::ObjectId, DateTime};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthData {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<ObjectId>,
#[serde(rename = "healthDataId")]
pub health_data_id: String,
#[serde(rename = "userId")]
pub user_id: String,
#[serde(rename = "profileId")]
pub profile_id: String,
#[serde(rename = "familyId")]
pub family_id: Option<String>,
#[serde(rename = "healthData")]
pub health_data: Vec<EncryptedField>,
#[serde(rename = "createdAt")]
pub created_at: DateTime,
#[serde(rename = "updatedAt")]
pub updated_at: DateTime,
#[serde(rename = "dataSource")]
pub data_source: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EncryptedField {
pub encrypted: bool,
pub data: String,
pub iv: String,
pub auth_tag: String,
}

View file

@ -0,0 +1,21 @@
use serde::{Deserialize, Serialize};
use mongodb::bson::{oid::ObjectId, DateTime};
use super::health_data::EncryptedField;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LabResult {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<ObjectId>,
#[serde(rename = "labResultId")]
pub lab_result_id: String,
#[serde(rename = "userId")]
pub user_id: String,
#[serde(rename = "profileId")]
pub profile_id: String,
#[serde(rename = "labData")]
pub lab_data: EncryptedField,
#[serde(rename = "createdAt")]
pub created_at: DateTime,
#[serde(rename = "updatedAt")]
pub updated_at: DateTime,
}

View file

@ -0,0 +1,31 @@
use serde::{Deserialize, Serialize};
use mongodb::bson::{oid::ObjectId, DateTime};
use super::health_data::EncryptedField;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Medication {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<ObjectId>,
#[serde(rename = "medicationId")]
pub medication_id: String,
#[serde(rename = "userId")]
pub user_id: String,
#[serde(rename = "profileId")]
pub profile_id: String,
#[serde(rename = "medicationData")]
pub medication_data: EncryptedField,
#[serde(rename = "reminders")]
pub reminders: Vec<MedicationReminder>,
#[serde(rename = "createdAt")]
pub created_at: DateTime,
#[serde(rename = "updatedAt")]
pub updated_at: DateTime,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MedicationReminder {
#[serde(rename = "reminderId")]
pub reminder_id: String,
#[serde(rename = "scheduledTime")]
pub scheduled_time: String,
}

View file

@ -0,0 +1,9 @@
pub mod user;
pub mod family;
pub mod profile;
pub mod health_data;
pub mod lab_result;
pub mod medication;
pub mod appointment;
pub mod share;
pub mod refresh_token;

View file

@ -0,0 +1,49 @@
use serde::{Deserialize, Serialize};
use mongodb::{bson::{doc, oid::ObjectId, DateTime}, Collection};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Profile {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<ObjectId>,
#[serde(rename = "profileId")]
pub profile_id: String,
#[serde(rename = "userId")]
pub user_id: String,
#[serde(rename = "familyId")]
pub family_id: Option<String>,
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "nameIv")]
pub name_iv: String,
#[serde(rename = "nameAuthTag")]
pub name_auth_tag: String,
#[serde(rename = "role")]
pub role: String,
#[serde(rename = "permissions")]
pub permissions: Vec<String>,
#[serde(rename = "createdAt")]
pub created_at: DateTime,
#[serde(rename = "updatedAt")]
pub updated_at: DateTime,
}
pub struct ProfileRepository {
collection: Collection<Profile>,
}
impl ProfileRepository {
pub fn new(collection: Collection<Profile>) -> Self {
Self { collection }
}
pub async fn create(&self, profile: &Profile) -> mongodb::error::Result<()> {
self.collection.insert_one(profile, None).await?;
Ok(())
}
pub async fn find_by_profile_id(&self, profile_id: &str) -> mongodb::error::Result<Option<Profile>> {
self.collection
.find_one(doc! { "profileId": profile_id }, None)
.await
}
}

View file

@ -0,0 +1,22 @@
use serde::{Deserialize, Serialize};
use mongodb::bson::{oid::ObjectId, DateTime};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RefreshToken {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<ObjectId>,
#[serde(rename = "tokenId")]
pub token_id: String,
#[serde(rename = "userId")]
pub user_id: String,
#[serde(rename = "tokenHash")]
pub token_hash: String,
#[serde(rename = "expiresAt")]
pub expires_at: DateTime,
#[serde(rename = "createdAt")]
pub created_at: DateTime,
#[serde(rename = "revoked")]
pub revoked: bool,
#[serde(rename = "revokedAt")]
pub revoked_at: Option<DateTime>,
}

View file

@ -0,0 +1,24 @@
use serde::{Deserialize, Serialize};
use mongodb::bson::{oid::ObjectId, DateTime};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Share {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<ObjectId>,
#[serde(rename = "shareId")]
pub share_id: String,
#[serde(rename = "userId")]
pub user_id: String,
#[serde(rename = "encryptedDataKey")]
pub encrypted_data_key: String,
#[serde(rename = "dataKeyIv")]
pub data_key_iv: String,
#[serde(rename = "dataKeyAuthTag")]
pub data_key_auth_tag: String,
#[serde(rename = "expiresAt")]
pub expires_at: DateTime,
#[serde(rename = "createdAt")]
pub created_at: DateTime,
#[serde(rename = "accessCount")]
pub access_count: i32,
}

View file

@ -0,0 +1,88 @@
use serde::{Deserialize, Serialize};
use mongodb::{bson::{doc, oid::ObjectId, DateTime}, Collection};
use chrono::{Utc, TimeZone};
use validator::Validate;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<ObjectId>,
#[serde(rename = "userId")]
pub user_id: String,
#[serde(rename = "email")]
pub email: String,
#[serde(rename = "passwordHash")]
pub password_hash: String,
#[serde(rename = "encryptedRecoveryPhrase")]
pub encrypted_recovery_phrase: String,
#[serde(rename = "recoveryPhraseIv")]
pub recovery_phrase_iv: String,
#[serde(rename = "recoveryPhraseAuthTag")]
pub recovery_phrase_auth_tag: String,
#[serde(rename = "tokenVersion")]
pub token_version: i32,
#[serde(rename = "familyId")]
pub family_id: Option<String>,
#[serde(rename = "profileIds")]
pub profile_ids: Vec<String>,
#[serde(rename = "createdAt")]
pub created_at: DateTime,
#[serde(rename = "updatedAt")]
pub updated_at: DateTime,
}
#[derive(Debug, Deserialize, Validate)]
pub struct RegisterUserRequest {
#[validate(email)]
pub email: String,
pub password_hash: String,
pub encrypted_recovery_phrase: String,
pub recovery_phrase_iv: String,
pub recovery_phrase_auth_tag: String,
}
#[derive(Debug, Deserialize, Validate)]
pub struct LoginRequest {
#[validate(email)]
pub email: String,
pub password_hash: String,
}
pub struct UserRepository {
collection: Collection<User>,
}
impl UserRepository {
pub fn new(collection: Collection<User>) -> Self {
Self { collection }
}
pub async fn create(&self, user: &User) -> mongodb::error::Result<()> {
self.collection.insert_one(user, None).await?;
Ok(())
}
pub async fn find_by_email(&self, email: &str) -> mongodb::error::Result<Option<User>> {
self.collection
.find_one(doc! { "email": email }, None)
.await
}
pub async fn find_by_user_id(&self, user_id: &str) -> mongodb::error::Result<Option<User>> {
self.collection
.find_one(doc! { "userId": user_id }, None)
.await
}
pub async fn update_token_version(&self, user_id: &str, version: i32) -> mongodb::error::Result<()> {
let now = DateTime::now();
self.collection
.update_one(
doc! { "userId": user_id },
doc! { "$set": { "tokenVersion": version, "updatedAt": now } },
None,
)
.await?;
Ok(())
}
}