Skip to main content

revolt_database/models/accounts/ops/
mongodb.rs

1use crate::{AbstractAccounts, Account, MongoDb};
2use bson::{to_bson, to_document};
3use iso8601_timestamp::Timestamp;
4use mongodb::options::{Collation, CollationStrength, FindOneOptions, UpdateOptions};
5use revolt_result::Result;
6
7const COL: &str = "accounts";
8
9#[async_trait]
10impl AbstractAccounts for MongoDb {
11    /// Find account by id
12    async fn fetch_account(&self, id: &str) -> Result<Account> {
13        query!(self, find_one_by_id, COL, id)?.ok_or_else(|| create_error!(UnknownUser))
14    }
15
16    /// Find account by normalised email
17    async fn fetch_account_by_normalised_email(
18        &self,
19        normalised_email: &str,
20    ) -> Result<Option<Account>> {
21        query!(
22            self,
23            find_one_with_options,
24            COL,
25            doc! {
26                "email_normalised": normalised_email
27            },
28            FindOneOptions::builder()
29                .collation(
30                    Collation::builder()
31                        .locale("en")
32                        .strength(CollationStrength::Secondary)
33                        .build(),
34                )
35                .build()
36        )
37    }
38
39    /// Find account with active pending email verification
40    async fn fetch_account_with_email_verification(&self, token: &str) -> Result<Account> {
41        query!(
42            self,
43            find_one,
44            COL,
45            doc! {
46                "verification.token": token,
47                "verification.expiry": {
48                    "$gte": to_bson(&Timestamp::now_utc()).unwrap()
49                }
50            }
51        )?
52        .ok_or_else(|| create_error!(InvalidToken))
53    }
54
55    /// Find account with active password reset
56    async fn fetch_account_with_password_reset(&self, token: &str) -> Result<Account> {
57        query!(
58            self,
59            find_one,
60            COL,
61            doc! {
62                "password_reset.token": token,
63                "password_reset.expiry": {
64                    "$gte": to_bson(&Timestamp::now_utc()).unwrap()
65                }
66            }
67        )?
68        .ok_or_else(|| create_error!(InvalidToken))
69    }
70
71    /// Find account with active deletion token
72    async fn fetch_account_with_deletion_token(&self, token: &str) -> Result<Account> {
73        query!(
74            self,
75            find_one,
76            COL,
77            doc! {
78                "deletion.token": token,
79                "deletion.expiry": {
80                    "$gte": to_bson(&Timestamp::now_utc()).unwrap()
81                }
82            }
83        )?
84        .ok_or_else(|| create_error!(InvalidToken))
85    }
86
87    /// Find accounts which are due to be deleted
88    async fn fetch_accounts_due_for_deletion(&self) -> Result<Vec<Account>> {
89        query!(
90            self,
91            find,
92            COL,
93            doc! {
94                "deletion.status": "Scheduled",
95                "deletion.after": {
96                    "$lte": to_bson(&Timestamp::now_utc()).unwrap()
97                }
98            }
99        )
100    }
101
102    // Save account
103    async fn save_account(&self, account: &Account) -> Result<()> {
104        self.col::<Account>(COL)
105            .update_one(
106                doc! {
107                    "_id": &account.id
108                },
109                doc! {
110                    "$set": to_document(account).map_err(|_| create_database_error!("to_document", COL))?
111                },
112            )
113            .with_options(UpdateOptions::builder().upsert(true).build())
114            .await
115            .map_err(|_| create_database_error!("find_one", COL))
116            .map(|_| ())
117    }
118}