Skip to main content

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