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