1use lru::LruCache;
7use std::num::NonZeroUsize;
8use std::sync::Arc;
9
10use crate::prelude::*;
11use cloudillo_types::meta_adapter::MetaAdapter;
12
13use super::types::{
14 FrozenSettingsRegistry, Setting, SettingDefinition, SettingScope, SettingValue,
15};
16
17const DEFAULT_CACHE_CAPACITY: NonZeroUsize = match NonZeroUsize::new(100) {
19 Some(n) => n,
20 None => unreachable!(),
21};
22
23pub struct SettingsCache {
25 cache: Arc<parking_lot::RwLock<LruCache<(TnId, String), SettingValue>>>,
26}
27
28impl SettingsCache {
29 pub fn new(capacity: usize) -> Self {
30 let non_zero = NonZeroUsize::new(capacity).unwrap_or(DEFAULT_CACHE_CAPACITY);
31 Self { cache: Arc::new(parking_lot::RwLock::new(LruCache::new(non_zero))) }
32 }
33
34 pub fn get(&self, tn_id: TnId, key: &str) -> Option<SettingValue> {
35 let mut cache = self.cache.write();
36 cache.get(&(tn_id, key.to_string())).cloned()
37 }
38
39 pub fn put(&self, tn_id: TnId, key: String, value: SettingValue) {
40 let mut cache = self.cache.write();
41 cache.put((tn_id, key), value);
42 }
43
44 pub fn clear(&self) {
46 let mut cache = self.cache.write();
47 cache.clear();
48 }
49
50 pub fn invalidate_key(&self, _key: &str) {
52 self.clear();
55 }
56}
57
58pub struct SettingsService {
60 registry: Arc<FrozenSettingsRegistry>,
61 cache: SettingsCache,
62 meta: Arc<dyn MetaAdapter>,
63}
64
65impl SettingsService {
66 pub fn new(
67 registry: Arc<FrozenSettingsRegistry>,
68 meta: Arc<dyn MetaAdapter>,
69 cache_size: usize,
70 ) -> Self {
71 Self { registry, cache: SettingsCache::new(cache_size), meta }
72 }
73
74 pub async fn get(&self, tn_id: TnId, key: &str) -> ClResult<SettingValue> {
76 if let Some(value) = self.cache.get(tn_id, key) {
78 debug!("Setting cache hit: {}.{}", tn_id.0, key);
79 return Ok(value);
80 }
81
82 let def = self
84 .registry
85 .get(key)
86 .ok_or_else(|| Error::ValidationError(format!("Unknown setting: {}", key)))?;
87
88 if tn_id.0 != 0 {
90 if let Some(json_value) = self.meta.read_setting(tn_id, key).await? {
91 let value = serde_json::from_value::<SettingValue>(json_value)
92 .map_err(|e| Error::ValidationError(format!("Invalid setting value: {}", e)))?;
93 self.cache.put(tn_id, key.to_string(), value.clone());
94 return Ok(value);
95 }
96 }
97
98 if let Some(json_value) = self.meta.read_setting(TnId(0), key).await? {
100 let value = serde_json::from_value::<SettingValue>(json_value)
101 .map_err(|e| Error::ValidationError(format!("Invalid setting value: {}", e)))?;
102 self.cache.put(tn_id, key.to_string(), value.clone());
103 return Ok(value);
104 }
105
106 match &def.default {
108 Some(default) => {
109 let value = default.clone();
110 self.cache.put(tn_id, key.to_string(), value.clone());
111 Ok(value)
112 }
113 None => Err(Error::ValidationError(format!(
114 "Setting '{}' has no default and must be configured",
115 key
116 ))),
117 }
118 }
119
120 pub async fn set<S: AsRef<str>>(
123 &self,
124 tn_id: TnId,
125 key: &str,
126 value: SettingValue,
127 roles: &[S],
128 ) -> ClResult<Setting> {
129 let def = self
131 .registry
132 .get(key)
133 .ok_or_else(|| Error::ValidationError(format!("Unknown setting: {}", key)))?;
134
135 if !def.permission.check(roles) {
137 warn!("Permission denied for setting '{}': requires {:?}", key, def.permission);
138 return Err(Error::PermissionDenied);
139 }
140
141 let storage_tn_id = match (def.scope, tn_id.0) {
144 (SettingScope::System, _) => {
145 return Err(Error::PermissionDenied);
146 }
147 (SettingScope::Global, 0) => {
148 TnId(0)
150 }
151 (SettingScope::Global, _) => {
152 if !roles.iter().any(|r| r.as_ref() == "SADM") {
155 return Err(Error::PermissionDenied);
156 }
157 TnId(0)
158 }
159 (SettingScope::Tenant, 0) => {
160 TnId(0)
163 }
164 (SettingScope::Tenant, _) => {
165 tn_id
167 }
168 };
169
170 if let Some(default) = &def.default {
172 if !value.matches_type(default) {
173 return Err(Error::ValidationError(format!(
174 "Type mismatch for setting '{}': expected {}, got {}",
175 key,
176 default.type_name(),
177 value.type_name()
178 )));
179 }
180 }
181
182 if let Some(validator) = &def.validator {
184 validator(&value)?;
185 }
186
187 let json_value = serde_json::to_value(&value)
189 .map_err(|e| Error::ValidationError(format!("Failed to serialize setting: {}", e)))?;
190 self.meta.update_setting(storage_tn_id, key, Some(json_value)).await?;
191
192 if storage_tn_id.0 == 0 {
194 self.cache.invalidate_key(key);
196 } else {
197 self.cache.clear();
199 }
200
201 info!("Setting '{}' updated for tn_id={}", key, storage_tn_id.0);
202
203 Ok(Setting {
205 key: key.to_string(),
206 value,
207 tn_id: storage_tn_id,
208 updated_at: cloudillo_types::types::Timestamp::now(),
209 })
210 }
211
212 pub async fn delete(&self, tn_id: TnId, key: &str) -> ClResult<bool> {
214 self.meta.update_setting(tn_id, key, None).await?;
215 self.cache.clear();
216
217 info!("Setting '{}' deleted for tn_id={}", key, tn_id.0);
218 Ok(true)
219 }
220
221 pub async fn validate_required_settings(&self) -> ClResult<()> {
223 for def in self.registry.list() {
224 if def.optional || def.default.is_some() {
226 continue;
227 }
228
229 if self.meta.read_setting(TnId(0), &def.key).await?.is_none() {
231 return Err(Error::ValidationError(format!(
232 "Required setting '{}' is not configured",
233 def.key
234 )));
235 }
236 }
237 Ok(())
238 }
239
240 pub async fn get_string(&self, tn_id: TnId, key: &str) -> ClResult<String> {
242 match self.get(tn_id, key).await? {
243 SettingValue::String(s) => Ok(s),
244 v => Err(Error::ValidationError(format!(
245 "Setting '{}' is not a string, got {}",
246 key,
247 v.type_name()
248 ))),
249 }
250 }
251
252 pub async fn get_int(&self, tn_id: TnId, key: &str) -> ClResult<i64> {
253 match self.get(tn_id, key).await? {
254 SettingValue::Int(i) => Ok(i),
255 v => Err(Error::ValidationError(format!(
256 "Setting '{}' is not an integer, got {}",
257 key,
258 v.type_name()
259 ))),
260 }
261 }
262
263 pub async fn get_bool(&self, tn_id: TnId, key: &str) -> ClResult<bool> {
264 match self.get(tn_id, key).await? {
265 SettingValue::Bool(b) => Ok(b),
266 v => Err(Error::ValidationError(format!(
267 "Setting '{}' is not a boolean, got {}",
268 key,
269 v.type_name()
270 ))),
271 }
272 }
273
274 pub async fn get_json(&self, tn_id: TnId, key: &str) -> ClResult<serde_json::Value> {
275 match self.get(tn_id, key).await? {
276 SettingValue::Json(j) => Ok(j),
277 v => Err(Error::ValidationError(format!(
278 "Setting '{}' is not JSON, got {}",
279 key,
280 v.type_name()
281 ))),
282 }
283 }
284
285 pub async fn get_string_opt(&self, tn_id: TnId, key: &str) -> ClResult<Option<String>> {
288 match self.get(tn_id, key).await {
289 Ok(SettingValue::String(s)) => Ok(Some(s)),
290 Ok(v) => Err(Error::ValidationError(format!(
291 "Setting '{}' is not a string, got {}",
292 key,
293 v.type_name()
294 ))),
295 Err(Error::ValidationError(msg)) if msg.contains("has no default") => Ok(None),
296 Err(Error::ValidationError(msg)) if msg.contains("Unknown setting") => Ok(None),
297 Err(e) => Err(e),
298 }
299 }
300
301 pub async fn get_int_opt(&self, tn_id: TnId, key: &str) -> ClResult<Option<i64>> {
302 match self.get(tn_id, key).await {
303 Ok(SettingValue::Int(i)) => Ok(Some(i)),
304 Ok(v) => Err(Error::ValidationError(format!(
305 "Setting '{}' is not an integer, got {}",
306 key,
307 v.type_name()
308 ))),
309 Err(Error::ValidationError(msg)) if msg.contains("has no default") => Ok(None),
310 Err(Error::ValidationError(msg)) if msg.contains("Unknown setting") => Ok(None),
311 Err(e) => Err(e),
312 }
313 }
314
315 pub async fn get_bool_opt(&self, tn_id: TnId, key: &str) -> ClResult<Option<bool>> {
316 match self.get(tn_id, key).await {
317 Ok(SettingValue::Bool(b)) => Ok(Some(b)),
318 Ok(v) => Err(Error::ValidationError(format!(
319 "Setting '{}' is not a boolean, got {}",
320 key,
321 v.type_name()
322 ))),
323 Err(Error::ValidationError(msg)) if msg.contains("has no default") => Ok(None),
324 Err(Error::ValidationError(msg)) if msg.contains("Unknown setting") => Ok(None),
325 Err(e) => Err(e),
326 }
327 }
328
329 pub async fn get_json_opt(
330 &self,
331 tn_id: TnId,
332 key: &str,
333 ) -> ClResult<Option<serde_json::Value>> {
334 match self.get(tn_id, key).await {
335 Ok(SettingValue::Json(j)) => Ok(Some(j)),
336 Ok(v) => Err(Error::ValidationError(format!(
337 "Setting '{}' is not JSON, got {}",
338 key,
339 v.type_name()
340 ))),
341 Err(Error::ValidationError(msg)) if msg.contains("has no default") => Ok(None),
342 Err(Error::ValidationError(msg)) if msg.contains("Unknown setting") => Ok(None),
343 Err(e) => Err(e),
344 }
345 }
346
347 pub fn registry(&self) -> &Arc<FrozenSettingsRegistry> {
349 &self.registry
350 }
351
352 pub async fn list_by_prefix(
358 &self,
359 tn_id: TnId,
360 prefix: &str,
361 ) -> ClResult<Vec<(String, SettingValue, &SettingDefinition)>> {
362 let prefixes = vec![format!("{}.", prefix)]; let global_settings = self.meta.list_settings(TnId(0), Some(&prefixes)).await?;
366
367 let tenant_settings = if tn_id.0 != 0 {
369 self.meta.list_settings(tn_id, Some(&prefixes)).await?
370 } else {
371 std::collections::HashMap::new()
372 };
373
374 let mut merged = global_settings;
376 merged.extend(tenant_settings);
377
378 let mut result = Vec::new();
379 for (key, json_value) in merged {
380 if let Some(definition) = self.registry.get(&key) {
381 let value = serde_json::from_value::<SettingValue>(json_value)
382 .map_err(|e| Error::ValidationError(format!("Invalid setting value: {}", e)))?;
383 result.push((key, value, definition));
384 }
385 }
386
387 Ok(result)
388 }
389}
390
391