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 #[serde(default = "default_enable_invite_codes")]
18 pub enable_invite_codes: bool,
19}
20
21fn default_security_registration_enabled() -> bool {
22 true
23}
24
25fn default_real_ip_header() -> String {
26 "CF-Connecting-IP".to_string()
27}
28
29fn default_enable_invite_codes() -> bool {
30 false
31}
32
33impl Default for SecurityConfig {
34 fn default() -> Self {
35 Self {
36 registration_enabled: default_security_registration_enabled(),
37 real_ip_header: default_real_ip_header(),
38 enable_invite_codes: default_enable_invite_codes(),
39 }
40 }
41}
42
43#[derive(Clone, Serialize, Deserialize, Debug)]
45pub struct DirsConfig {
46 #[serde(default = "default_dir_templates")]
48 pub templates: String,
49 #[serde(default = "default_dir_assets")]
51 pub assets: String,
52 #[serde(default = "default_dir_media")]
54 pub media: String,
55 #[serde(default = "default_dir_icons")]
57 pub icons: String,
58 #[serde(default = "default_dir_docs")]
60 pub docs: String,
61 #[serde(default = "default_dir_rustdoc")]
64 pub rustdoc: String,
65}
66
67fn default_dir_templates() -> String {
68 "html".to_string()
69}
70
71fn default_dir_assets() -> String {
72 "public".to_string()
73}
74
75fn default_dir_media() -> String {
76 "media".to_string()
77}
78
79fn default_dir_icons() -> String {
80 "icons".to_string()
81}
82
83fn default_dir_docs() -> String {
84 "docs".to_string()
85}
86
87fn default_dir_rustdoc() -> String {
88 "reference".to_string()
89}
90
91impl Default for DirsConfig {
92 fn default() -> Self {
93 Self {
94 templates: default_dir_templates(),
95 assets: default_dir_assets(),
96 media: default_dir_media(),
97 icons: default_dir_icons(),
98 docs: default_dir_docs(),
99 rustdoc: default_dir_rustdoc(),
100 }
101 }
102}
103
104impl Configuration for Config {
105 fn db_config(&self) -> DatabaseConfig {
106 self.database.to_owned()
107 }
108}
109
110#[derive(Clone, Serialize, Deserialize, Debug)]
112pub struct PoliciesConfig {
113 pub terms_of_service: String,
119 pub privacy: String,
124 #[serde(default)]
132 pub last_updated: usize,
133}
134
135impl Default for PoliciesConfig {
136 fn default() -> Self {
137 Self {
138 terms_of_service: "/public/tos.html".to_string(),
139 privacy: "/public/privacy.html".to_string(),
140 last_updated: 0,
141 }
142 }
143}
144
145#[derive(Clone, Serialize, Deserialize, Debug)]
147pub struct TurnstileConfig {
148 pub site_key: String,
149 pub secret_key: String,
150}
151
152impl Default for TurnstileConfig {
153 fn default() -> Self {
154 Self {
155 site_key: "1x00000000000000000000AA".to_string(), secret_key: "1x0000000000000000000000000000000AA".to_string(), }
158 }
159}
160
161#[derive(Clone, Serialize, Deserialize, Debug, Default)]
162pub struct ConnectionsConfig {
163 #[serde(default)]
165 pub spotify_client_id: Option<String>,
166 #[serde(default)]
168 pub last_fm_key: Option<String>,
169 #[serde(default)]
171 pub last_fm_secret: Option<String>,
172}
173
174#[derive(Clone, Serialize, Deserialize, Debug, Default)]
185pub struct StripeConfig {
186 pub secret: String,
188 pub payment_links: StripePaymentLinks,
195 pub webhook_signing_secret: String,
203 pub billing_portal_url: String,
207 pub price_texts: StripePriceTexts,
209 pub product_ids: StripeProductIds,
213 pub price_ids: StripePriceIds,
215}
216
217#[derive(Clone, Serialize, Deserialize, Debug, Default)]
218pub struct StripePriceTexts {
219 pub supporter: String,
220 pub dev_pass: String,
221 pub coins_100: String,
222 pub coins_400: String,
223}
224
225#[derive(Clone, Serialize, Deserialize, Debug, Default)]
226pub struct StripePaymentLinks {
227 pub supporter: String,
228 pub dev_pass: String,
229}
230
231#[derive(Clone, Serialize, Deserialize, Debug, Default)]
232pub struct StripeProductIds {
233 pub supporter: String,
234 pub dev_pass: String,
235 pub coins_100: String,
236 pub coins_400: String,
237}
238
239#[derive(Clone, Serialize, Deserialize, Debug, Default)]
240pub struct StripePriceIds {
241 pub coins_100: String,
242 pub coins_400: String,
243}
244
245#[derive(Clone, Serialize, Deserialize, Debug)]
247pub struct ManualsConfig {
248 pub search_help: String,
250}
251
252impl Default for ManualsConfig {
253 fn default() -> Self {
254 Self {
255 search_help: "".to_string(),
256 }
257 }
258}
259
260#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
261pub enum StringBan {
262 String(String),
264 Unicode(u32),
266}
267
268impl Default for StringBan {
269 fn default() -> Self {
270 Self::String(String::new())
271 }
272}
273
274#[derive(Clone, Serialize, Deserialize, Debug)]
276pub struct Config {
277 #[serde(default = "default_name")]
279 pub name: String,
280 #[serde(default = "default_description")]
282 pub description: String,
283 #[serde(default = "default_color")]
285 pub color: String,
286 #[serde(default = "default_port")]
288 pub port: u16,
289 #[serde(default = "default_banned_hosts")]
296 pub banned_hosts: Vec<String>,
297 #[serde(default = "default_host")]
300 pub host: String,
301 #[serde(default = "default_lw_host")]
304 pub lw_host: String,
305 #[serde(default = "default_security")]
307 pub security: SecurityConfig,
308 #[serde(default = "default_dirs")]
310 pub dirs: DirsConfig,
311 #[serde(default = "default_database")]
313 pub database: DatabaseConfig,
314 #[serde(default = "default_no_track")]
317 pub no_track: Vec<String>,
318 #[serde(default = "default_banned_usernames")]
320 pub banned_usernames: Vec<String>,
321 #[serde(default = "default_policies")]
323 pub policies: PoliciesConfig,
324 #[serde(default = "default_turnstile")]
326 pub turnstile: TurnstileConfig,
327 #[serde(default)]
332 pub town_square: usize,
333 #[serde(default)]
335 pub town_square_forum: usize,
336 #[serde(default)]
339 pub town_square_forum_topic: usize,
340 #[serde(default)]
342 pub system_user: usize,
343 #[serde(default)]
344 pub connections: ConnectionsConfig,
345 #[serde(default)]
348 pub html_footer_path: String,
349 #[serde(default)]
350 pub stripe: Option<StripeConfig>,
351 #[serde(default)]
353 pub manuals: ManualsConfig,
354 #[serde(default)]
356 pub banned_data: Vec<StringBan>,
357 #[serde(default)]
359 pub enable_user_ads: bool,
360}
361
362fn default_name() -> String {
363 "Tetratto".to_string()
364}
365
366fn default_description() -> String {
367 "🐇 tetratto!".to_string()
368}
369
370fn default_color() -> String {
371 "#c9b1bc".to_string()
372}
373fn default_port() -> u16 {
374 4118
375}
376
377fn default_banned_hosts() -> Vec<String> {
378 Vec::new()
379}
380
381fn default_host() -> String {
382 String::new()
383}
384
385fn default_lw_host() -> String {
386 String::new()
387}
388
389fn default_security() -> SecurityConfig {
390 SecurityConfig::default()
391}
392
393fn default_dirs() -> DirsConfig {
394 DirsConfig::default()
395}
396
397fn default_database() -> DatabaseConfig {
398 DatabaseConfig::default()
399}
400
401fn default_no_track() -> Vec<String> {
402 Vec::new()
403}
404
405fn default_banned_usernames() -> Vec<String> {
406 vec![
407 "admin".to_string(),
408 "owner".to_string(),
409 "moderator".to_string(),
410 "api".to_string(),
411 "communities".to_string(),
412 "community".to_string(),
413 "notifs".to_string(),
414 "notification".to_string(),
415 "post".to_string(),
416 "void".to_string(),
417 "anonymous".to_string(),
418 "stacks".to_string(),
419 "stack".to_string(),
420 "search".to_string(),
421 "journals".to_string(),
422 "links".to_string(),
423 "app".to_string(),
424 "services".to_string(),
425 "domains".to_string(),
426 "mail".to_string(),
427 "product".to_string(),
428 "wallet".to_string(),
429 "products".to_string(),
430 ]
431}
432
433fn default_policies() -> PoliciesConfig {
434 PoliciesConfig::default()
435}
436
437fn default_turnstile() -> TurnstileConfig {
438 TurnstileConfig::default()
439}
440
441fn default_connections() -> ConnectionsConfig {
442 ConnectionsConfig::default()
443}
444
445fn default_manuals() -> ManualsConfig {
446 ManualsConfig::default()
447}
448
449fn default_banned_data() -> Vec<StringBan> {
450 Vec::new()
451}
452
453impl Default for Config {
454 fn default() -> Self {
455 Self {
456 name: default_name(),
457 description: default_description(),
458 color: default_color(),
459 port: default_port(),
460 banned_hosts: default_banned_hosts(),
461 host: default_host(),
462 lw_host: default_lw_host(),
463 database: default_database(),
464 security: default_security(),
465 dirs: default_dirs(),
466 no_track: default_no_track(),
467 banned_usernames: default_banned_usernames(),
468 policies: default_policies(),
469 turnstile: default_turnstile(),
470 town_square: 0,
471 town_square_forum: 0,
472 town_square_forum_topic: 0,
473 system_user: 0,
474 connections: default_connections(),
475 html_footer_path: String::new(),
476 stripe: None,
477 manuals: default_manuals(),
478 banned_data: default_banned_data(),
479 enable_user_ads: false,
480 }
481 }
482}
483
484impl Config {
485 pub fn read(contents: String) -> Self {
487 toml::from_str::<Self>(&contents).unwrap()
488 }
489
490 pub fn get_config() -> Self {
492 let path = PathBufD::current().join("tetratto.toml");
493
494 match fs::read_to_string(&path) {
495 Ok(c) => Config::read(c),
496 Err(_) => {
497 Self::update_config(Self::default()).expect("failed to write default config");
498 Self::default()
499 }
500 }
501 }
502
503 pub fn update_config(contents: Self) -> Result<()> {
505 let c = fs::canonicalize(".").unwrap();
506 let here = c.to_str().unwrap();
507
508 fs::write(
509 format!("{here}/tetratto.toml"),
510 toml::to_string_pretty::<Self>(&contents).unwrap(),
511 )
512 }
513}