revolt_database/drivers/
mod.rs

1#[cfg(feature = "mongodb")]
2mod mongodb;
3mod reference;
4
5use authifier::config::Captcha;
6use authifier::config::EmailVerificationConfig;
7use authifier::config::PasswordScanning;
8use authifier::config::ResolveIp;
9use authifier::config::SMTPSettings;
10use authifier::config::Shield;
11use authifier::config::Template;
12use authifier::config::Templates;
13use authifier::Authifier;
14use rand::Rng;
15use revolt_config::config;
16
17#[cfg(feature = "mongodb")]
18pub use self::mongodb::*;
19pub use self::reference::*;
20
21/// Database information to use to create a client
22pub enum DatabaseInfo {
23    /// Auto-detect the database in use
24    Auto,
25    /// Auto-detect the database in use and create an empty testing database
26    Test(String),
27    /// Use the mock database
28    Reference,
29    /// Connect to MongoDB
30    #[cfg(feature = "mongodb")]
31    MongoDb { uri: String, database_name: String },
32    /// Use existing MongoDB connection
33    #[cfg(feature = "mongodb")]
34    MongoDbFromClient(::mongodb::Client, String),
35}
36
37/// Database
38#[derive(Clone, Debug)]
39pub enum Database {
40    /// Mock database
41    Reference(ReferenceDb),
42    /// MongoDB database
43    #[cfg(feature = "mongodb")]
44    MongoDb(MongoDb),
45}
46
47impl DatabaseInfo {
48    /// Create a database client from the given database information
49    #[async_recursion]
50    pub async fn connect(self) -> Result<Database, String> {
51        let config = config().await;
52
53        match self {
54            DatabaseInfo::Auto => {
55                if std::env::var("TEST_DB").is_ok() {
56                    DatabaseInfo::Test(format!(
57                        "revolt_test_{}",
58                        rand::thread_rng().gen_range(1_000_000..10_000_000)
59                    ))
60                    .connect()
61                    .await
62                } else if !config.database.mongodb.is_empty() {
63                    #[cfg(feature = "mongodb")]
64                    return DatabaseInfo::MongoDb {
65                        uri: config.database.mongodb,
66                        database_name: "revolt".to_string(),
67                    }
68                    .connect()
69                    .await;
70
71                    #[cfg(not(feature = "mongodb"))]
72                    return Err("MongoDB not enabled.".to_string())
73                } else {
74                    DatabaseInfo::Reference.connect().await
75                }
76            }
77            DatabaseInfo::Test(database_name) => {
78                match std::env::var("TEST_DB")
79                    .expect("`TEST_DB` environment variable should be set to REFERENCE or MONGODB")
80                    .as_str()
81                {
82                    "REFERENCE" => DatabaseInfo::Reference.connect().await,
83                    "MONGODB" => {
84                        #[cfg(feature = "mongodb")]
85                        return DatabaseInfo::MongoDb {
86                            uri: config.database.mongodb,
87                            database_name,
88                        }
89                        .connect()
90                        .await;
91
92                        #[cfg(not(feature = "mongodb"))]
93                        return Err("MongoDB not enabled.".to_string())
94                    }
95                    _ => unreachable!("must specify REFERENCE or MONGODB"),
96                }
97            }
98            DatabaseInfo::Reference => Ok(Database::Reference(Default::default())),
99            #[cfg(feature = "mongodb")]
100            DatabaseInfo::MongoDb { uri, database_name } => {
101                let client = ::mongodb::Client::with_uri_str(uri)
102                    .await
103                    .map_err(|_| "Failed to init db connection.".to_string())?;
104
105                Ok(Database::MongoDb(MongoDb(client, database_name)))
106            }
107            #[cfg(feature = "mongodb")]
108            DatabaseInfo::MongoDbFromClient(client, database_name) => {
109                Ok(Database::MongoDb(MongoDb(client, database_name)))
110            }
111        }
112    }
113}
114
115impl Database {
116    /// Create an Authifier reference
117    pub async fn to_authifier(self) -> Authifier {
118        let config = config().await;
119
120        let mut auth_config = authifier::Config {
121            password_scanning: if config.api.security.easypwned.is_empty() {
122                Default::default()
123            } else {
124                PasswordScanning::EasyPwned {
125                    endpoint: config.api.security.easypwned,
126                }
127            },
128            email_verification: if !config.api.smtp.host.is_empty() {
129                EmailVerificationConfig::Enabled {
130                    smtp: SMTPSettings {
131                        from: config.api.smtp.from_address,
132                        host: config.api.smtp.host,
133                        username: config.api.smtp.username,
134                        password: config.api.smtp.password,
135                        reply_to: Some(
136                            config
137                                .api
138                                .smtp
139                                .reply_to
140                                .unwrap_or("support@revolt.chat".into()),
141                        ),
142                        port: config.api.smtp.port,
143                        use_tls: config.api.smtp.use_tls,
144                        use_starttls: config.api.smtp.use_starttls,
145                    },
146                    expiry: Default::default(),
147                    templates: if config.production {
148                        Templates {
149                            verify: Template {
150                                title: "Verify your Revolt account.".into(),
151                                text: include_str!("../../templates/verify.txt").into(),
152                                url: format!("{}/login/verify/", config.hosts.app),
153                                html: Some(include_str!("../../templates/verify.html").into()),
154                            },
155                            reset: Template {
156                                title: "Reset your Revolt password.".into(),
157                                text: include_str!("../../templates/reset.txt").into(),
158                                url: format!("{}/login/reset/", config.hosts.app),
159                                html: Some(include_str!("../../templates/reset.html").into()),
160                            },
161                            reset_existing: Template {
162                                title: "You already have a Revolt account, reset your password."
163                                    .into(),
164                                text: include_str!("../../templates/reset-existing.txt").into(),
165                                url: format!("{}/login/reset/", config.hosts.app),
166                                html: Some(
167                                    include_str!("../../templates/reset-existing.html").into(),
168                                ),
169                            },
170                            deletion: Template {
171                                title: "Confirm account deletion.".into(),
172                                text: include_str!("../../templates/deletion.txt").into(),
173                                url: format!("{}/delete/", config.hosts.app),
174                                html: Some(include_str!("../../templates/deletion.html").into()),
175                            },
176                            welcome: None,
177                        }
178                    } else {
179                        Templates {
180                            verify: Template {
181                                title: "Verify your account.".into(),
182                                text: include_str!("../../templates/verify.whitelabel.txt").into(),
183                                url: format!("{}/login/verify/", config.hosts.app),
184                                html: None,
185                            },
186                            reset: Template {
187                                title: "Reset your password.".into(),
188                                text: include_str!("../../templates/reset.whitelabel.txt").into(),
189                                url: format!("{}/login/reset/", config.hosts.app),
190                                html: None,
191                            },
192                            reset_existing: Template {
193                                title: "Reset your password.".into(),
194                                text: include_str!("../../templates/reset.whitelabel.txt").into(),
195                                url: format!("{}/login/reset/", config.hosts.app),
196                                html: None,
197                            },
198                            deletion: Template {
199                                title: "Confirm account deletion.".into(),
200                                text: include_str!("../../templates/deletion.whitelabel.txt")
201                                    .into(),
202                                url: format!("{}/delete/", config.hosts.app),
203                                html: None,
204                            },
205                            welcome: None,
206                        }
207                    },
208                }
209            } else {
210                EmailVerificationConfig::Disabled
211            },
212            ..Default::default()
213        };
214
215        auth_config.invite_only = config.api.registration.invite_only;
216
217        if !config.api.security.captcha.hcaptcha_key.is_empty() {
218            auth_config.captcha = Captcha::HCaptcha {
219                secret: config.api.security.captcha.hcaptcha_key,
220            };
221        }
222
223        if !config.api.security.authifier_shield_key.is_empty() {
224            auth_config.shield = Shield::Enabled {
225                api_key: config.api.security.authifier_shield_key,
226                strict: false,
227            };
228        }
229
230        if config.api.security.trust_cloudflare {
231            auth_config.resolve_ip = ResolveIp::Cloudflare;
232        }
233
234        Authifier {
235            database: match self {
236                Database::Reference(_) => Default::default(),
237                #[cfg(feature = "mongodb")]
238                Database::MongoDb(MongoDb(client, _)) => authifier::Database::MongoDb(
239                    authifier::database::MongoDb(client.database("revolt")),
240                ),
241            },
242            config: auth_config,
243            #[cfg(feature = "tasks")]
244            event_channel: Some(crate::tasks::authifier_relay::sender()),
245            #[cfg(not(feature = "tasks"))]
246            event_channel: None,
247        }
248    }
249}