1use crate::prelude::StringExt;
2
3use super::{
4 ExtensionSettings, SettingsDeserializeExt, SettingsDeserializer, SettingsSerializeExt,
5 SettingsSerializer,
6};
7use compact_str::ToCompactString;
8use serde::{Deserialize, Serialize};
9use utoipa::ToSchema;
10
11#[derive(ToSchema, Serialize, Deserialize, Clone, Copy)]
12#[serde(rename_all = "snake_case")]
13pub enum TwoFactorRequirement {
14 Admins,
15 AllUsers,
16 None,
17}
18
19#[derive(ToSchema, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
20#[serde(rename_all = "snake_case")]
21pub enum TwoFactorMethod {
22 Totp,
23 SecurityKey,
24 Email,
25}
26
27impl TwoFactorMethod {
28 pub const ALL: &'static [TwoFactorMethod] = &[
29 TwoFactorMethod::Totp,
30 TwoFactorMethod::SecurityKey,
31 TwoFactorMethod::Email,
32 ];
33 pub const DEFAULT_ACCEPTED: &'static [TwoFactorMethod] =
34 &[TwoFactorMethod::Totp, TwoFactorMethod::SecurityKey];
35}
36
37#[derive(Clone, ToSchema, Serialize, Deserialize)]
38pub struct AppSettingsApp {
39 pub name: compact_str::CompactString,
40 pub icon: compact_str::CompactString,
41 pub icon_light: Option<compact_str::CompactString>,
42 pub banner: Option<compact_str::CompactString>,
43 pub banner_light: Option<compact_str::CompactString>,
44 pub url: compact_str::CompactString,
45 pub language: compact_str::CompactString,
46 pub two_factor_requirement: TwoFactorRequirement,
47 pub email_two_factor_enabled: bool,
48 pub two_factor_accepted_methods: Vec<TwoFactorMethod>,
49
50 pub email_verification_required: bool,
51
52 pub session_cookie: compact_str::CompactString,
53 pub session_duration_seconds: u64,
54
55 pub telemetry_enabled: bool,
56 pub registration_enabled: bool,
57}
58
59#[async_trait::async_trait]
60impl SettingsSerializeExt for AppSettingsApp {
61 async fn serialize(
62 &self,
63 serializer: SettingsSerializer,
64 ) -> Result<SettingsSerializer, anyhow::Error> {
65 Ok(serializer
66 .write_raw_setting("name", &*self.name)
67 .write_raw_setting("icon", &*self.icon)
68 .write_raw_setting("icon_light", self.icon_light.as_deref().unwrap_or(""))
69 .write_raw_setting("banner", self.banner.as_deref().unwrap_or(""))
70 .write_raw_setting("banner_light", self.banner_light.as_deref().unwrap_or(""))
71 .write_raw_setting("url", &*self.url)
72 .write_raw_setting("language", &*self.language)
73 .write_raw_setting(
74 "two_factor_requirement",
75 match self.two_factor_requirement {
76 TwoFactorRequirement::Admins => "admins",
77 TwoFactorRequirement::AllUsers => "all_users",
78 TwoFactorRequirement::None => "none",
79 },
80 )
81 .write_raw_setting(
82 "email_two_factor_enabled",
83 self.email_two_factor_enabled.to_compact_string(),
84 )
85 .write_serde_setting(
86 "two_factor_accepted_methods",
87 &self.two_factor_accepted_methods,
88 )?
89 .write_raw_setting(
90 "email_verification_required",
91 self.email_verification_required.to_compact_string(),
92 )
93 .write_raw_setting("session_cookie", &*self.session_cookie)
94 .write_raw_setting(
95 "session_duration_seconds",
96 self.session_duration_seconds.to_compact_string(),
97 )
98 .write_raw_setting(
99 "telemetry_enabled",
100 self.telemetry_enabled.to_compact_string(),
101 )
102 .write_raw_setting(
103 "registration_enabled",
104 self.registration_enabled.to_compact_string(),
105 ))
106 }
107}
108
109pub struct AppSettingsAppDeserializer;
110
111#[async_trait::async_trait]
112impl SettingsDeserializeExt for AppSettingsAppDeserializer {
113 async fn deserialize_boxed(
114 &self,
115 mut deserializer: SettingsDeserializer<'_>,
116 ) -> Result<ExtensionSettings, anyhow::Error> {
117 Ok(Box::new(AppSettingsApp {
118 name: deserializer
119 .take_raw_setting("name")
120 .unwrap_or_else(|| "Calagopus".into()),
121 icon: deserializer
122 .take_raw_setting("icon")
123 .unwrap_or_else(|| "/icon.svg".into()),
124 icon_light: deserializer
125 .take_raw_setting("icon_light")
126 .and_then(|s| s.into_optional()),
127 banner: deserializer
128 .take_raw_setting("banner")
129 .and_then(|s| s.into_optional()),
130 banner_light: deserializer
131 .take_raw_setting("banner_light")
132 .and_then(|s| s.into_optional()),
133 url: deserializer
134 .take_raw_setting("url")
135 .unwrap_or_else(|| "http://localhost:8000".into()),
136 language: deserializer
137 .take_raw_setting("language")
138 .unwrap_or_else(|| "en".into()),
139 two_factor_requirement: match deserializer
140 .take_raw_setting("two_factor_requirement")
141 .as_deref()
142 {
143 Some("admins") => TwoFactorRequirement::Admins,
144 Some("all_users") => TwoFactorRequirement::AllUsers,
145 _ => TwoFactorRequirement::None,
146 },
147 email_two_factor_enabled: deserializer
148 .take_raw_setting("email_two_factor_enabled")
149 .map(|s| s == "true")
150 .unwrap_or(false),
151 two_factor_accepted_methods: deserializer
152 .read_serde_setting("two_factor_accepted_methods")
153 .unwrap_or_else(|_| TwoFactorMethod::DEFAULT_ACCEPTED.to_vec()),
154 email_verification_required: deserializer
155 .take_raw_setting("email_verification_required")
156 .map(|s| s == "true")
157 .unwrap_or(false),
158 session_cookie: deserializer
159 .take_raw_setting("session_cookie")
160 .unwrap_or_else(|| "calagopus_session".into()),
161 session_duration_seconds: deserializer
162 .take_raw_setting("session_duration_seconds")
163 .and_then(|s| s.parse::<u64>().ok())
164 .unwrap_or(7 * 24 * 3600),
165 telemetry_enabled: deserializer
166 .take_raw_setting("telemetry_enabled")
167 .map(|s| s == "true")
168 .unwrap_or(true),
169 registration_enabled: deserializer
170 .take_raw_setting("registration_enabled")
171 .map(|s| s == "true")
172 .unwrap_or(true),
173 }))
174 }
175}