revolt_database/drivers/
mod.rs

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