1use oiseau::config::{Configuration, DatabaseConfig};
2use pathbufd::PathBufD;
3use serde::{Deserialize, Serialize};
4use std::fs;
5use std::io::Result;
6
7#[derive(Clone, Serialize, Deserialize, Debug)]
9pub struct SecurityConfig {
10 #[serde(default = "default_security_registration_enabled")]
12 pub registration_enabled: bool,
13 #[serde(default = "default_real_ip_header")]
15 pub real_ip_header: String,
16}
17
18fn default_security_registration_enabled() -> bool {
19 true
20}
21
22fn default_real_ip_header() -> String {
23 "CF-Connecting-IP".to_string()
24}
25
26impl Default for SecurityConfig {
27 fn default() -> Self {
28 Self {
29 registration_enabled: default_security_registration_enabled(),
30 real_ip_header: default_real_ip_header(),
31 }
32 }
33}
34
35#[derive(Clone, Serialize, Deserialize, Debug)]
37pub struct DirsConfig {
38 #[serde(default = "default_dir_templates")]
40 pub templates: String,
41 #[serde(default = "default_dir_assets")]
43 pub assets: String,
44 #[serde(default = "default_dir_media")]
46 pub media: String,
47 #[serde(default = "default_dir_icons")]
49 pub icons: String,
50 #[serde(default = "default_dir_rustdoc")]
53 pub rustdoc: String,
54}
55
56fn default_dir_templates() -> String {
57 "html".to_string()
58}
59
60fn default_dir_assets() -> String {
61 "public".to_string()
62}
63
64fn default_dir_media() -> String {
65 "media".to_string()
66}
67
68fn default_dir_icons() -> String {
69 "icons".to_string()
70}
71
72fn default_dir_rustdoc() -> String {
73 "reference".to_string()
74}
75
76impl Default for DirsConfig {
77 fn default() -> Self {
78 Self {
79 templates: default_dir_templates(),
80 assets: default_dir_assets(),
81 media: default_dir_media(),
82 icons: default_dir_icons(),
83 rustdoc: default_dir_rustdoc(),
84 }
85 }
86}
87
88impl Configuration for Config {
89 fn db_config(&self) -> DatabaseConfig {
90 self.database.to_owned()
91 }
92}
93
94#[derive(Clone, Serialize, Deserialize, Debug)]
96pub struct PoliciesConfig {
97 pub terms_of_service: String,
103 pub privacy: String,
108 pub refunds: String,
110 #[serde(default)]
118 pub last_updated: usize,
119}
120
121impl Default for PoliciesConfig {
122 fn default() -> Self {
123 Self {
124 terms_of_service: "/public/tos.html".to_string(),
125 privacy: "/public/privacy.html".to_string(),
126 refunds: "/public/refunds.html".to_string(),
127 last_updated: 0,
128 }
129 }
130}
131
132#[derive(Clone, Serialize, Deserialize, Debug)]
134pub struct TurnstileConfig {
135 pub site_key: String,
136 pub secret_key: String,
137}
138
139impl Default for TurnstileConfig {
140 fn default() -> Self {
141 Self {
142 site_key: "1x00000000000000000000AA".to_string(), secret_key: "1x0000000000000000000000000000000AA".to_string(), }
145 }
146}
147
148#[derive(Clone, Serialize, Deserialize, Debug, Default)]
149pub struct ConnectionsConfig {
150 #[serde(default)]
153 pub last_fm_key: Option<String>,
154 #[serde(default)]
157 pub last_fm_secret: Option<String>,
158}
159
160#[derive(Clone, Serialize, Deserialize, Debug, Default)]
171pub struct StripeConfig {
172 pub secret: String,
174 pub payment_links: StripePaymentLinks,
181 pub webhook_signing_secret: String,
189 pub billing_portal_url: String,
193 pub price_texts: StripePriceTexts,
195 pub product_ids: StripeProductIds,
199}
200
201#[derive(Clone, Serialize, Deserialize, Debug, Default)]
202pub struct StripePriceTexts {
203 pub supporter: String,
204}
205
206#[derive(Clone, Serialize, Deserialize, Debug, Default)]
207pub struct StripePaymentLinks {
208 pub supporter: String,
209}
210
211#[derive(Clone, Serialize, Deserialize, Debug, Default)]
212pub struct StripeProductIds {
213 pub supporter: String,
214}
215
216#[derive(Clone, Serialize, Deserialize, Debug)]
218pub struct ManualsConfig {
219 pub search_help: String,
221}
222
223impl Default for ManualsConfig {
224 fn default() -> Self {
225 Self {
226 search_help: "".to_string(),
227 }
228 }
229}
230
231#[derive(Clone, Serialize, Deserialize, Debug, Default)]
232pub struct ServiceHostsConfig {
233 pub buckets: String,
235 #[serde(default)]
237 pub tawny: String,
238 #[serde(default)]
240 pub dashboard: String,
241 #[serde(default = "default_overkit")]
242 pub overkit: String,
243}
244
245fn default_overkit() -> String {
246 "http://localhost:8026".to_string()
247}
248
249#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
250pub enum StringBan {
251 String(String),
253 Unicode(u32),
255}
256
257impl Default for StringBan {
258 fn default() -> Self {
259 Self::String(String::new())
260 }
261}
262
263#[derive(Clone, Serialize, Deserialize, Debug)]
265pub struct Config {
266 #[serde(default = "default_name")]
268 pub name: String,
269 #[serde(default = "default_description")]
271 pub description: String,
272 #[serde(default = "default_port")]
274 pub port: u16,
275 #[serde(default = "default_banned_hosts")]
282 pub banned_hosts: Vec<String>,
283 #[serde(default = "default_host")]
286 pub host: String,
287 #[serde(default = "default_service_hosts")]
289 pub service_hosts: ServiceHostsConfig,
290 #[serde(default = "default_security")]
292 pub security: SecurityConfig,
293 #[serde(default = "default_dirs")]
295 pub dirs: DirsConfig,
296 #[serde(default = "default_database")]
298 pub database: DatabaseConfig,
299 #[serde(default = "default_banned_usernames")]
301 pub banned_usernames: Vec<String>,
302 #[serde(default = "default_policies")]
304 pub policies: PoliciesConfig,
305 #[serde(default = "default_turnstile")]
307 pub turnstile: TurnstileConfig,
308 #[serde(default)]
309 pub connections: ConnectionsConfig,
310 #[serde(default)]
311 pub stripe: Option<StripeConfig>,
312 #[serde(default)]
314 pub manuals: ManualsConfig,
315 #[serde(default)]
317 pub banned_data: Vec<StringBan>,
318 #[serde(default)]
320 pub persistent_banner: String,
321}
322
323fn default_name() -> String {
324 "Tetratto".to_string()
325}
326
327fn default_description() -> String {
328 "Tetratto (next-gen)".to_string()
329}
330
331fn default_port() -> u16 {
332 4118
333}
334
335fn default_banned_hosts() -> Vec<String> {
336 Vec::new()
337}
338
339fn default_host() -> String {
340 String::new()
341}
342
343fn default_service_hosts() -> ServiceHostsConfig {
344 ServiceHostsConfig::default()
345}
346
347fn default_security() -> SecurityConfig {
348 SecurityConfig::default()
349}
350
351fn default_dirs() -> DirsConfig {
352 DirsConfig::default()
353}
354
355fn default_database() -> DatabaseConfig {
356 DatabaseConfig::default()
357}
358
359fn default_banned_usernames() -> Vec<String> {
360 vec![
361 "admin".to_string(),
362 "owner".to_string(),
363 "moderator".to_string(),
364 "api".to_string(),
365 "communities".to_string(),
366 "community".to_string(),
367 "notification".to_string(),
368 "post".to_string(),
369 "anonymous".to_string(),
370 "search".to_string(),
371 "app".to_string(),
372 ]
373}
374
375fn default_policies() -> PoliciesConfig {
376 PoliciesConfig::default()
377}
378
379fn default_turnstile() -> TurnstileConfig {
380 TurnstileConfig::default()
381}
382
383fn default_connections() -> ConnectionsConfig {
384 ConnectionsConfig::default()
385}
386
387fn default_manuals() -> ManualsConfig {
388 ManualsConfig::default()
389}
390
391fn default_banned_data() -> Vec<StringBan> {
392 Vec::new()
393}
394
395impl Default for Config {
396 fn default() -> Self {
397 Self {
398 name: default_name(),
399 description: default_description(),
400 port: default_port(),
401 banned_hosts: default_banned_hosts(),
402 host: default_host(),
403 service_hosts: default_service_hosts(),
404 database: default_database(),
405 security: default_security(),
406 dirs: default_dirs(),
407 banned_usernames: default_banned_usernames(),
408 policies: default_policies(),
409 turnstile: default_turnstile(),
410 connections: default_connections(),
411 stripe: None,
412 manuals: default_manuals(),
413 banned_data: default_banned_data(),
414 persistent_banner: String::new(),
415 }
416 }
417}
418
419impl Config {
420 pub fn read(contents: String) -> Self {
422 toml::from_str::<Self>(&contents).unwrap()
423 }
424
425 pub fn get_config() -> Self {
427 let path = PathBufD::current().join("app.toml");
428
429 match fs::read_to_string(&path) {
430 Ok(c) => Config::read(c),
431 Err(_) => {
432 Self::update_config(Self::default()).expect("failed to write default config");
433 Self::default()
434 }
435 }
436 }
437
438 pub fn update_config(contents: Self) -> Result<()> {
440 let c = fs::canonicalize(".").unwrap();
441 let here = c.to_str().unwrap();
442
443 fs::write(
444 format!("{here}/app.toml"),
445 toml::to_string_pretty::<Self>(&contents).unwrap(),
446 )
447 }
448}