autter_core/
config.rs

1use oiseau::config::{Configuration, DatabaseConfig};
2use pathbufd::PathBufD;
3use serde::{Deserialize, Serialize};
4
5#[derive(Clone, Debug, Serialize, Deserialize)]
6pub struct Config {
7    /// The name of the site. Shown in the UI.
8    #[serde(default = "default_name")]
9    pub name: String,
10    /// Database configuration.
11    #[serde(default = "default_database")]
12    pub database: DatabaseConfig,
13    /// Service hosts config.
14    #[serde(default)]
15    pub service_hosts: ServiceHostsConfig,
16    /// The public URL of this service.
17    #[serde(default)]
18    pub host: String,
19    /// Usernames which cannot be used by any user.
20    #[serde(default = "default_banned_usernames")]
21    pub banned_usernames: Vec<String>,
22    /// Security settings.
23    #[serde(default)]
24    pub security: SecurityConfig,
25    /// Directories config.
26    #[serde(default)]
27    pub dirs: DirectoriesConfig,
28    /// Stripe payments config.
29    pub stripe: StripeConfig,
30}
31
32fn default_banned_usernames() -> Vec<String> {
33    vec!["admin".to_string(), "settings".to_string()]
34}
35
36#[derive(Clone, Debug, Serialize, Deserialize, Default)]
37pub struct SecurityConfig {
38    #[serde(default)]
39    pub accepting_purchases: bool,
40    #[serde(default)]
41    pub registration_enabled: bool,
42    /// Real IP header (for reverse proxy).
43    #[serde(default = "default_real_ip_header")]
44    pub real_ip_header: String,
45    /// The hostnames of approved login redirect destinations.
46    #[serde(default)]
47    pub approved_login_redirects: Vec<String>,
48}
49
50#[derive(Clone, Debug, Serialize, Deserialize)]
51pub struct ServiceHostsConfig {
52    #[serde(default = "default_shrimpcamp")]
53    pub shrimpcamp: String,
54    #[serde(default = "default_tetratto")]
55    pub tetratto: String,
56    #[serde(default = "default_buckets")]
57    pub buckets: String,
58    #[serde(default = "default_askall")]
59    pub askall: String,
60    #[serde(default = "default_issuestack")]
61    pub issuestack: String,
62    #[serde(default = "default_fluffle")]
63    pub fluffle: String,
64}
65
66fn default_shrimpcamp() -> String {
67    "https://about.shrimpcamp.com".to_string()
68}
69
70fn default_tetratto() -> String {
71    "https://tetratto.com".to_string()
72}
73
74fn default_askall() -> String {
75    "https://askall.cc".to_string()
76}
77
78fn default_issuestack() -> String {
79    "https://stack.shrimpcamp.com".to_string()
80}
81
82fn default_fluffle() -> String {
83    "https://fluffle.cc".to_string()
84}
85
86fn default_buckets() -> String {
87    "http://localhost:8020".to_string()
88}
89
90impl Default for ServiceHostsConfig {
91    fn default() -> Self {
92        Self {
93            shrimpcamp: default_shrimpcamp(),
94            tetratto: default_tetratto(),
95            buckets: default_buckets(),
96            askall: default_askall(),
97            issuestack: default_issuestack(),
98            fluffle: default_fluffle(),
99        }
100    }
101}
102
103#[derive(Clone, Debug, Serialize, Deserialize)]
104pub struct DirectoriesConfig {
105    #[serde(default = "default_media")]
106    pub media: String,
107}
108
109fn default_media() -> String {
110    "media".to_string()
111}
112
113impl Default for DirectoriesConfig {
114    fn default() -> Self {
115        Self {
116            media: default_media(),
117        }
118    }
119}
120
121fn default_name() -> String {
122    "Autter".to_string()
123}
124
125fn default_real_ip_header() -> String {
126    "CF-Connecting-IP".to_string()
127}
128
129fn default_database() -> DatabaseConfig {
130    DatabaseConfig::default()
131}
132
133/// Configuration for Stripe integration.
134///
135/// User IDs are sent to Stripe through the payment link.
136/// <https://docs.stripe.com/payment-links/url-parameters#streamline-reconciliation-with-a-url-parameter>
137///
138/// # Testing
139///
140/// - Run `stripe login` using the Stripe CLI
141/// - Run `stripe listen --forward-to localhost:4118/api/v1/service_hooks/stripe`
142/// - Use testing card numbers: <https://docs.stripe.com/testing?testing-method=card-numbers#visa>
143#[derive(Clone, Serialize, Deserialize, Debug, Default)]
144pub struct StripeConfig {
145    /// Your Stripe API secret.
146    pub secret: String,
147    /// To apply benefits to user accounts, you should then go into the Stripe developer
148    /// "workbench" and create a new webhook. The webhook needs the scopes:
149    /// `invoice.payment_succeeded`, `customer.subscription.deleted`, `checkout.session.completed`, `charge.succeeded`.
150    ///
151    /// The webhook's destination address should be `{your server origin}/api/v1/service_hooks/stripe`.
152    ///
153    /// The signing secret can be found on the right after you have created the webhook.
154    pub webhook_signing_secret: String,
155    /// The URL of your customer billing portal.
156    ///
157    /// <https://docs.stripe.com/no-code/customer-portal>
158    pub billing_portal_url: String,
159    /// The text representation of prices. (like `$4 USD`)
160    pub price_texts: StripePriceTexts,
161    /// Product IDs from the Stripe dashboard.
162    ///
163    /// These are checked when we receive a webhook to ensure we provide the correct product.
164    pub product_ids: StripeProductIds,
165    /// The IDs of individual prices for products which require us to generate sessions ourselves.
166    pub price_ids: StripePriceIds,
167}
168
169#[derive(Clone, Serialize, Deserialize, Debug, Default)]
170pub struct StripePriceTexts {
171    pub organization: String,
172    pub user_reg: String,
173}
174
175#[derive(Clone, Serialize, Deserialize, Debug, Default)]
176pub struct StripeProductIds {
177    pub organization: String,
178    pub user_reg: String,
179}
180
181#[derive(Clone, Serialize, Deserialize, Debug, Default)]
182pub struct StripePriceIds {
183    pub organization: String,
184    pub user_reg: String,
185}
186
187impl Configuration for Config {
188    fn db_config(&self) -> DatabaseConfig {
189        self.database.to_owned()
190    }
191}
192
193impl Default for Config {
194    fn default() -> Self {
195        Self {
196            name: default_name(),
197            database: default_database(),
198            service_hosts: ServiceHostsConfig::default(),
199            host: String::new(),
200            banned_usernames: default_banned_usernames(),
201            security: SecurityConfig::default(),
202            dirs: DirectoriesConfig::default(),
203            stripe: StripeConfig::default(),
204        }
205    }
206}
207
208impl Config {
209    /// Read the configuration file.
210    pub fn read() -> Self {
211        toml::from_str(
212            &match std::fs::read_to_string(PathBufD::current().join("app.toml")) {
213                Ok(x) => x,
214                Err(_) => {
215                    let x = Config::default();
216
217                    std::fs::write(
218                        PathBufD::current().join("app.toml"),
219                        toml::to_string_pretty(&x).expect("failed to serialize config"),
220                    )
221                    .expect("failed to write config");
222
223                    return x;
224                }
225            },
226        )
227        .expect("failed to deserialize config")
228    }
229}