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}