backend/
config.rs

1use crate::error::Error;
2use bunny_api_tokio::edge_storage::Endpoint;
3use lettre::transport::smtp::authentication::Credentials;
4use log::debug;
5use serde::Deserialize;
6use tokio::fs::read_to_string;
7use url::Url;
8use uuid::Uuid;
9
10#[derive(Debug, Deserialize)]
11pub struct ConfigBuilder {
12    database: Database,
13    cache_database: CacheDatabase,
14    web: WebBuilder,
15    instance: Option<InstanceBuilder>,
16    bunny: BunnyBuilder,
17    mail: Mail,
18}
19
20#[derive(Debug, Deserialize, Clone)]
21pub struct Database {
22    username: String,
23    password: String,
24    host: String,
25    database: String,
26    port: u16,
27}
28
29#[derive(Debug, Deserialize, Clone)]
30pub struct CacheDatabase {
31    username: Option<String>,
32    password: Option<String>,
33    host: String,
34    database: Option<String>,
35    port: u16,
36}
37
38#[derive(Debug, Deserialize)]
39struct WebBuilder {
40    ip: Option<String>,
41    port: Option<u16>,
42    frontend_url: Url,
43    backend_url: Option<Url>,
44    _ssl: Option<bool>,
45}
46
47#[derive(Debug, Deserialize)]
48struct InstanceBuilder {
49    name: Option<String>,
50    registration: Option<bool>,
51    require_email_verification: Option<bool>,
52    initial_guild: Option<Uuid>,
53}
54
55#[derive(Debug, Deserialize)]
56struct BunnyBuilder {
57    api_key: String,
58    endpoint: String,
59    storage_zone: String,
60    cdn_url: Url,
61}
62
63#[derive(Debug, Deserialize, Clone)]
64pub struct Mail {
65    pub smtp: Smtp,
66    pub address: String,
67    pub tls: String,
68}
69
70#[derive(Debug, Deserialize, Clone)]
71pub struct Smtp {
72    pub server: String,
73    username: String,
74    password: String,
75}
76
77impl ConfigBuilder {
78    pub async fn load(path: String) -> Result<Self, Error> {
79        debug!("loading config from: {path}");
80        let raw = read_to_string(path).await?;
81
82        let config = toml::from_str(&raw)?;
83
84        Ok(config)
85    }
86
87    pub fn build(self) -> Config {
88        let web = Web {
89            ip: self.web.ip.unwrap_or(String::from("0.0.0.0")),
90            port: self.web.port.unwrap_or(8080),
91            frontend_url: self.web.frontend_url.clone(),
92            backend_url: self
93                .web
94                .backend_url
95                .or_else(|| self.web.frontend_url.join("/api").ok())
96                .unwrap(),
97        };
98
99        let endpoint = match &*self.bunny.endpoint {
100            "Frankfurt" => Endpoint::Frankfurt,
101            "London" => Endpoint::London,
102            "New York" => Endpoint::NewYork,
103            "Los Angeles" => Endpoint::LosAngeles,
104            "Singapore" => Endpoint::Singapore,
105            "Stockholm" => Endpoint::Stockholm,
106            "Sao Paulo" => Endpoint::SaoPaulo,
107            "Johannesburg" => Endpoint::Johannesburg,
108            "Sydney" => Endpoint::Sydney,
109            url => Endpoint::Custom(url.to_string()),
110        };
111
112        let bunny = Bunny {
113            api_key: self.bunny.api_key,
114            endpoint,
115            storage_zone: self.bunny.storage_zone,
116            cdn_url: self.bunny.cdn_url,
117        };
118
119        let instance = match self.instance {
120            Some(instance) => Instance {
121                name: instance.name.unwrap_or("Gorb".to_string()),
122                registration: instance.registration.unwrap_or(true),
123                require_email_verification: instance.require_email_verification.unwrap_or(false),
124                initial_guild: instance.initial_guild,
125            },
126            None => Instance {
127                name: "Gorb".to_string(),
128                registration: true,
129                require_email_verification: false,
130                initial_guild: None,
131            },
132        };
133
134        Config {
135            database: self.database,
136            cache_database: self.cache_database,
137            web,
138            instance,
139            bunny,
140            mail: self.mail,
141        }
142    }
143}
144
145#[derive(Debug, Clone)]
146pub struct Config {
147    pub database: Database,
148    pub cache_database: CacheDatabase,
149    pub web: Web,
150    pub instance: Instance,
151    pub bunny: Bunny,
152    pub mail: Mail,
153}
154
155#[derive(Debug, Clone)]
156pub struct Web {
157    pub ip: String,
158    pub port: u16,
159    pub frontend_url: Url,
160    pub backend_url: Url,
161}
162
163#[derive(Debug, Clone)]
164pub struct Instance {
165    pub name: String,
166    pub registration: bool,
167    pub require_email_verification: bool,
168    pub initial_guild: Option<Uuid>,
169}
170
171#[derive(Debug, Clone)]
172pub struct Bunny {
173    pub api_key: String,
174    pub endpoint: Endpoint,
175    pub storage_zone: String,
176    pub cdn_url: Url,
177}
178
179impl Database {
180    pub fn url(&self) -> String {
181        let mut url = String::from("postgres://");
182
183        url += &self.username;
184
185        url += ":";
186        url += &self.password;
187
188        url += "@";
189
190        url += &self.host;
191        url += ":";
192        url += &self.port.to_string();
193
194        url += "/";
195        url += &self.database;
196
197        url
198    }
199}
200
201impl CacheDatabase {
202    pub fn url(&self) -> String {
203        let mut url = String::from("redis://");
204
205        if let Some(username) = &self.username {
206            url += username;
207        }
208
209        if let Some(password) = &self.password {
210            url += ":";
211            url += password;
212        }
213
214        if self.username.is_some() || self.password.is_some() {
215            url += "@";
216        }
217
218        url += &self.host;
219        url += ":";
220        url += &self.port.to_string();
221
222        if let Some(database) = &self.database {
223            url += "/";
224            url += database;
225        }
226
227        url
228    }
229}
230
231impl Smtp {
232    pub fn credentials(&self) -> Credentials {
233        Credentials::new(self.username.clone(), self.password.clone())
234    }
235}