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 DefinitionMatch, 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 {
26 cache: Arc<parking_lot::Mutex<LruCache<(TnId, String), SettingValue>>>,
27}
28
29impl SettingsCache {
30 pub fn new(capacity: usize) -> Self {
31 let non_zero = NonZeroUsize::new(capacity).unwrap_or(DEFAULT_CACHE_CAPACITY);
32 Self { cache: Arc::new(parking_lot::Mutex::new(LruCache::new(non_zero))) }
33 }
34
35 pub fn get(&self, tn_id: TnId, key: &str) -> Option<SettingValue> {
36 let mut cache = self.cache.lock();
37 cache.get(&(tn_id, key.to_string())).cloned()
38 }
39
40 pub fn put(&self, tn_id: TnId, key: String, value: SettingValue) {
41 let mut cache = self.cache.lock();
42 cache.put((tn_id, key), value);
43 }
44
45 pub fn clear(&self) {
47 let mut cache = self.cache.lock();
48 cache.clear();
49 }
50
51 pub fn invalidate_key(&self, key: &str) {
55 let mut cache = self.cache.lock();
56 let to_remove: Vec<(TnId, String)> =
60 cache.iter().filter(|((_, k), _)| k == key).map(|(k, _)| k.clone()).collect();
61 for k in to_remove {
62 cache.pop(&k);
63 }
64 }
65}
66
67pub struct SettingsService {
69 registry: Arc<FrozenSettingsRegistry>,
70 cache: SettingsCache,
71 meta: Arc<dyn MetaAdapter>,
72}
73
74impl SettingsService {
75 pub fn new(
76 registry: Arc<FrozenSettingsRegistry>,
77 meta: Arc<dyn MetaAdapter>,
78 cache_size: usize,
79 ) -> Self {
80 Self { registry, cache: SettingsCache::new(cache_size), meta }
81 }
82
83 pub async fn get(&self, tn_id: TnId, key: &str) -> ClResult<Option<SettingValue>> {
93 if let Some(value) = self.cache.get(tn_id, key) {
95 debug!("Setting cache hit: {}.{}", tn_id.0, key);
96 return Ok(Some(value));
97 }
98 if tn_id.0 != 0
99 && let Some(value) = self.cache.get(TnId(0), key)
100 {
101 debug!("Setting cache hit (global fallback): {}", key);
102 return Ok(Some(value));
103 }
104
105 let m = self
107 .registry
108 .get_match(key)
109 .ok_or_else(|| Error::SettingNotFound(format!("Unknown setting: {}", key)))?;
110
111 if tn_id.0 != 0
113 && let Some(json_value) = self.meta.read_setting(tn_id, key).await?
114 {
115 let value = serde_json::from_value::<SettingValue>(json_value)
116 .map_err(|e| Error::ValidationError(format!("Invalid setting value: {}", e)))?;
117 self.cache.put(tn_id, key.to_string(), value.clone());
118 return Ok(Some(value));
119 }
120
121 if let Some(json_value) = self.meta.read_setting(TnId(0), key).await? {
123 let value = serde_json::from_value::<SettingValue>(json_value)
124 .map_err(|e| Error::ValidationError(format!("Invalid setting value: {}", e)))?;
125 self.cache.put(TnId(0), key.to_string(), value.clone());
126 return Ok(Some(value));
127 }
128
129 let def = match m {
130 DefinitionMatch::Exact(d) => d,
131 DefinitionMatch::Wildcard(_) => return Ok(None),
132 };
133 match &def.default {
134 Some(default) => {
135 let value = default.clone();
136 self.cache.put(tn_id, key.to_string(), value.clone());
137 Ok(Some(value))
138 }
139 None => Err(Error::SettingNotFound(format!(
140 "Setting '{}' has no default and must be configured",
141 key
142 ))),
143 }
144 }
145
146 pub async fn get_raw(&self, tn_id: TnId, key: &str) -> ClResult<Option<SettingValue>> {
157 self.registry
159 .get_match(key)
160 .ok_or_else(|| Error::SettingNotFound(format!("Unknown setting: {}", key)))?;
161
162 match self.meta.read_setting(tn_id, key).await? {
163 Some(json_value) => {
164 let value = serde_json::from_value::<SettingValue>(json_value)
165 .map_err(|e| Error::ValidationError(format!("Invalid setting value: {}", e)))?;
166 Ok(Some(value))
167 }
168 None => Ok(None),
169 }
170 }
171
172 pub async fn set<S: AsRef<str>>(
178 &self,
179 tn_id: TnId,
180 key: &str,
181 value: SettingValue,
182 roles: &[S],
183 ) -> ClResult<Setting> {
184 let def = self.definition(key)?;
186
187 if !def.permission.check(roles) {
189 warn!("Permission denied for setting '{}': requires {:?}", key, def.permission);
190 return Err(Error::PermissionDenied);
191 }
192
193 let storage_tn_id = match (def.scope, tn_id.0) {
203 (SettingScope::System, _) => {
204 return Err(Error::PermissionDenied);
205 }
206 (SettingScope::Global | SettingScope::Tenant, 0) => {
207 if !roles.iter().any(|r| r.as_ref() == "SADM") {
213 return Err(Error::PermissionDenied);
214 }
215 TnId(0)
216 }
217 (SettingScope::Global, _) => {
218 if !roles.iter().any(|r| r.as_ref() == "SADM") {
221 return Err(Error::PermissionDenied);
222 }
223 TnId(0)
224 }
225 (SettingScope::Tenant, _) => {
226 tn_id
229 }
230 };
231
232 self.write_at(def, storage_tn_id, key, value).await
233 }
234
235 pub async fn set_system(
245 &self,
246 tn_id: TnId,
247 key: &str,
248 value: SettingValue,
249 ) -> ClResult<Setting> {
250 let def = self.definition(key)?;
251
252 if def.scope != SettingScope::Tenant || tn_id.0 == 0 {
253 warn!(
254 "System write refused for setting '{}' (scope {:?}, tn_id={})",
255 key, def.scope, tn_id.0
256 );
257 return Err(Error::PermissionDenied);
258 }
259
260 self.write_at(def, tn_id, key, value).await
261 }
262
263 async fn write_at(
267 &self,
268 def: &SettingDefinition,
269 storage_tn_id: TnId,
270 key: &str,
271 value: SettingValue,
272 ) -> ClResult<Setting> {
273 if let Some(default) = &def.default
275 && !value.matches_type(default)
276 {
277 return Err(Error::ValidationError(format!(
278 "Type mismatch for setting '{}': expected {}, got {}",
279 key,
280 default.type_name(),
281 value.type_name()
282 )));
283 }
284
285 if let Some(validator) = &def.validator {
287 validator(&value)?;
288 }
289
290 let json_value = serde_json::to_value(&value)
292 .map_err(|e| Error::ValidationError(format!("Failed to serialize setting: {}", e)))?;
293 self.meta.update_setting(storage_tn_id, key, Some(json_value)).await?;
294
295 self.cache.invalidate_key(key);
299
300 info!("Setting '{}' updated for tn_id={}", key, storage_tn_id.0);
301
302 Ok(Setting {
304 key: key.to_string(),
305 value,
306 tn_id: storage_tn_id,
307 updated_at: cloudillo_types::types::Timestamp::now(),
308 })
309 }
310
311 pub async fn delete(&self, tn_id: TnId, key: &str) -> ClResult<bool> {
313 self.meta.update_setting(tn_id, key, None).await?;
314 self.cache.invalidate_key(key);
315
316 info!("Setting '{}' deleted for tn_id={}", key, tn_id.0);
317 Ok(true)
318 }
319
320 pub async fn clear<S: AsRef<str>>(&self, tn_id: TnId, key: &str, roles: &[S]) -> ClResult<()> {
325 let def = self.definition(key)?;
326
327 if !def.permission.check(roles) {
328 warn!(
329 "Permission denied for clearing setting '{}': requires {:?}",
330 key, def.permission
331 );
332 return Err(Error::PermissionDenied);
333 }
334
335 let storage_tn_id = match (def.scope, tn_id.0) {
343 (SettingScope::System, _) => return Err(Error::PermissionDenied),
344 (SettingScope::Global | SettingScope::Tenant, 0) => {
345 if !roles.iter().any(|r| r.as_ref() == "SADM") {
349 return Err(Error::PermissionDenied);
350 }
351 TnId(0)
352 }
353 (SettingScope::Global, _) => {
354 if !roles.iter().any(|r| r.as_ref() == "SADM") {
355 return Err(Error::PermissionDenied);
356 }
357 TnId(0)
358 }
359 (SettingScope::Tenant, _) => tn_id,
360 };
361
362 self.clear_at(storage_tn_id, key).await
363 }
364
365 pub async fn clear_system(&self, tn_id: TnId, key: &str) -> ClResult<()> {
375 let def = self.definition(key)?;
376
377 if def.scope != SettingScope::Tenant || tn_id.0 == 0 {
378 warn!(
379 "System clear refused for setting '{}' (scope {:?}, tn_id={})",
380 key, def.scope, tn_id.0
381 );
382 return Err(Error::PermissionDenied);
383 }
384
385 self.clear_at(tn_id, key).await
386 }
387
388 fn definition(&self, key: &str) -> ClResult<&SettingDefinition> {
390 self.registry
391 .get(key)
392 .ok_or_else(|| Error::ValidationError(format!("Unknown setting: {}", key)))
393 }
394
395 async fn clear_at(&self, storage_tn_id: TnId, key: &str) -> ClResult<()> {
397 self.meta.update_setting(storage_tn_id, key, None).await?;
398
399 self.cache.invalidate_key(key);
403
404 info!("Setting '{}' cleared for tn_id={}", key, storage_tn_id.0);
405 Ok(())
406 }
407
408 pub async fn validate_required_settings(&self) -> ClResult<()> {
410 for def in self.registry.list() {
411 if def.optional || def.default.is_some() {
413 continue;
414 }
415
416 if self.meta.read_setting(TnId(0), &def.key).await?.is_none() {
418 return Err(Error::ValidationError(format!(
419 "Required setting '{}' is not configured",
420 def.key
421 )));
422 }
423 }
424 Ok(())
425 }
426
427 pub async fn get_string(&self, tn_id: TnId, key: &str) -> ClResult<String> {
429 match self.get(tn_id, key).await? {
430 Some(SettingValue::String(s)) => Ok(s),
431 Some(v) => Err(Error::ValidationError(format!(
432 "Setting '{}' is not a string, got {}",
433 key,
434 v.type_name()
435 ))),
436 None => Err(Error::SettingNotFound(format!(
437 "Setting '{}' has no default and must be configured",
438 key
439 ))),
440 }
441 }
442
443 pub async fn get_int(&self, tn_id: TnId, key: &str) -> ClResult<i64> {
444 match self.get(tn_id, key).await? {
445 Some(SettingValue::Int(i)) => Ok(i),
446 Some(v) => Err(Error::ValidationError(format!(
447 "Setting '{}' is not an integer, got {}",
448 key,
449 v.type_name()
450 ))),
451 None => Err(Error::SettingNotFound(format!(
452 "Setting '{}' has no default and must be configured",
453 key
454 ))),
455 }
456 }
457
458 pub async fn get_bool(&self, tn_id: TnId, key: &str) -> ClResult<bool> {
459 match self.get(tn_id, key).await? {
460 Some(SettingValue::Bool(b)) => Ok(b),
461 Some(v) => Err(Error::ValidationError(format!(
462 "Setting '{}' is not a boolean, got {}",
463 key,
464 v.type_name()
465 ))),
466 None => Err(Error::SettingNotFound(format!(
467 "Setting '{}' has no default and must be configured",
468 key
469 ))),
470 }
471 }
472
473 pub async fn get_json(&self, tn_id: TnId, key: &str) -> ClResult<serde_json::Value> {
474 match self.get(tn_id, key).await? {
475 Some(SettingValue::Json(j)) => Ok(j),
476 Some(v) => Err(Error::ValidationError(format!(
477 "Setting '{}' is not JSON, got {}",
478 key,
479 v.type_name()
480 ))),
481 None => Err(Error::SettingNotFound(format!(
482 "Setting '{}' has no default and must be configured",
483 key
484 ))),
485 }
486 }
487
488 pub async fn get_string_opt(&self, tn_id: TnId, key: &str) -> ClResult<Option<String>> {
491 match self.get(tn_id, key).await {
492 Ok(Some(SettingValue::String(s))) => Ok(Some(s)),
493 Ok(Some(v)) => Err(Error::ValidationError(format!(
494 "Setting '{}' is not a string, got {}",
495 key,
496 v.type_name()
497 ))),
498 Ok(None) | Err(Error::SettingNotFound(_)) => Ok(None),
499 Err(e) => Err(e),
500 }
501 }
502
503 pub async fn get_int_opt(&self, tn_id: TnId, key: &str) -> ClResult<Option<i64>> {
504 match self.get(tn_id, key).await {
505 Ok(Some(SettingValue::Int(i))) => Ok(Some(i)),
506 Ok(Some(v)) => Err(Error::ValidationError(format!(
507 "Setting '{}' is not an integer, got {}",
508 key,
509 v.type_name()
510 ))),
511 Ok(None) | Err(Error::SettingNotFound(_)) => Ok(None),
512 Err(e) => Err(e),
513 }
514 }
515
516 pub async fn get_bool_opt(&self, tn_id: TnId, key: &str) -> ClResult<Option<bool>> {
517 match self.get(tn_id, key).await {
518 Ok(Some(SettingValue::Bool(b))) => Ok(Some(b)),
519 Ok(Some(v)) => Err(Error::ValidationError(format!(
520 "Setting '{}' is not a boolean, got {}",
521 key,
522 v.type_name()
523 ))),
524 Ok(None) | Err(Error::SettingNotFound(_)) => Ok(None),
525 Err(e) => Err(e),
526 }
527 }
528
529 pub async fn get_json_opt(
530 &self,
531 tn_id: TnId,
532 key: &str,
533 ) -> ClResult<Option<serde_json::Value>> {
534 match self.get(tn_id, key).await {
535 Ok(Some(SettingValue::Json(j))) => Ok(Some(j)),
536 Ok(Some(v)) => Err(Error::ValidationError(format!(
537 "Setting '{}' is not JSON, got {}",
538 key,
539 v.type_name()
540 ))),
541 Ok(None) | Err(Error::SettingNotFound(_)) => Ok(None),
542 Err(e) => Err(e),
543 }
544 }
545
546 pub fn registry(&self) -> &Arc<FrozenSettingsRegistry> {
548 &self.registry
549 }
550
551 pub async fn list_by_prefix(
557 &self,
558 tn_id: TnId,
559 prefixes: &[String],
560 ) -> ClResult<Vec<(String, SettingValue, &SettingDefinition)>> {
561 let prefixes_dotted: Vec<String> = prefixes.iter().map(|p| format!("{}.", p)).collect();
562
563 let global_settings = self.meta.list_settings(TnId(0), Some(&prefixes_dotted)).await?;
565
566 let tenant_settings = if tn_id.0 != 0 {
568 self.meta.list_settings(tn_id, Some(&prefixes_dotted)).await?
569 } else {
570 std::collections::HashMap::new()
571 };
572
573 let mut merged = global_settings;
575 merged.extend(tenant_settings);
576
577 let mut result = Vec::new();
578 for (key, json_value) in merged {
579 if let Some(definition) = self.registry.get(&key) {
580 let value = serde_json::from_value::<SettingValue>(json_value)
581 .map_err(|e| Error::ValidationError(format!("Invalid setting value: {}", e)))?;
582 result.push((key, value, definition));
583 }
584 }
585
586 Ok(result)
587 }
588
589 pub async fn list_by_prefix_at(
592 &self,
593 tn_id: TnId,
594 prefixes: &[String],
595 ) -> ClResult<Vec<(String, SettingValue, &SettingDefinition)>> {
596 let prefixes_dotted: Vec<String> = prefixes.iter().map(|p| format!("{}.", p)).collect();
597 let rows = self.meta.list_settings(tn_id, Some(&prefixes_dotted)).await?;
598 let mut result = Vec::new();
599 for (key, json_value) in rows {
600 if let Some(definition) = self.registry.get(&key) {
601 let value = serde_json::from_value::<SettingValue>(json_value)
602 .map_err(|e| Error::ValidationError(format!("Invalid setting value: {}", e)))?;
603 result.push((key, value, definition));
604 }
605 }
606 Ok(result)
607 }
608}
609
610