rainbeam/
config.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! Application config manager
use serde::{Deserialize, Serialize};
use std::io::Result;

use authbeam::database::HCaptchaConfig;
use rainbeam_shared::fs;

/// Premium features
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Tiers {
    /// Doubled character limits for everything
    ///
    /// * Questions: ~~2048~~ **4096**
    /// * Responses: ~~4096~~ **8192**
    /// * Comments: ~~2048~~ **4096**
    ///
    /// *\*Carpgraph drawings stay at 32kb maximum*
    #[serde(default)]
    pub double_limits: i32,
    /// Styled profile card in the followers/following/friends section of other users
    #[serde(default)]
    pub stylish_card: i32,
    /// A small little crown shown on the user's profile avatar
    #[serde(default)]
    pub avatar_crown: i32,
    /// A small badge shwon on the user's profile
    #[serde(default)]
    pub profile_badge: i32,
}

impl Default for Tiers {
    /// Everything is tier 1 by default
    fn default() -> Self {
        Self {
            double_limits: 1,
            stylish_card: 1,
            avatar_crown: 1,
            profile_badge: 1,
        }
    }
}

/// Configuration file
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Config {
    /// The port to serve the server on
    pub port: u16,
    /// The name of the site
    pub name: String,
    /// The description of the site
    pub description: String,
    /// The location of the static directory, should not be supplied manually as it will be overwritten with `./.config/static`
    #[serde(default)]
    pub static_dir: String,
    /// HCaptcha configuration
    pub captcha: HCaptchaConfig,
    /// The name of the header used for reading user IP address
    pub real_ip_header: Option<String>,
    /// If new profile registration is enabled
    #[serde(default)]
    pub registration_enabled: bool,
    /// The origin of the public server (ex: "https://rainbeam.net")
    ///
    /// Used in embeds and links.
    #[serde(default)]
    pub host: String,
    /// The hostname of the public server (for Citrus)
    ///
    /// Same as `host`, just without the protocol.
    #[serde(default)]
    pub citrus_id: String,
    /// A list of image hosts that are blocked
    #[serde(default)]
    pub blocked_hosts: Vec<String>,
    /// If a migration should be run
    #[serde(default)]
    pub migration: bool,
    /// Tiered benefits
    #[serde(default)]
    pub tiers: Tiers,
    /// A global site announcement shown at the top of the page
    #[serde(default)]
    pub alert: String,
    /// If Citrus should use https or http
    #[serde(default = "authbeam::database::secure_default")]
    pub secure: bool,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            port: 8080,
            name: "Rainbeam".to_string(),
            description: "Ask, share, socialize!".to_string(),
            static_dir: String::new(),
            captcha: HCaptchaConfig::default(),
            real_ip_header: Option::None,
            registration_enabled: true,
            host: String::new(),
            citrus_id: String::new(),
            blocked_hosts: Vec::new(),
            migration: false,
            tiers: Tiers::default(),
            alert: String::new(),
            secure: true,
        }
    }
}

impl Config {
    /// Read configuration file into [`Config`]
    pub fn read(contents: String) -> Self {
        toml::from_str::<Self>(&contents).unwrap()
    }

    /// Pull configuration file
    pub fn get_config() -> Self {
        let c = fs::canonicalize(".").unwrap();
        let here = c.to_str().unwrap();

        match fs::read(format!("{here}/.config/config.toml")) {
            Ok(c) => Config::read(c),
            Err(_) => {
                Self::update_config(Self::default()).expect("failed to write default config");
                Self::default()
            }
        }
    }

    /// Update configuration file
    pub fn update_config(contents: Self) -> Result<()> {
        let c = fs::canonicalize(".").unwrap();
        let here = c.to_str().unwrap();

        fs::write(
            format!("{here}/.config/config.toml"),
            toml::to_string_pretty::<Self>(&contents).unwrap(),
        )
    }
}