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}