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>>(
175 &self,
176 tn_id: TnId,
177 key: &str,
178 value: SettingValue,
179 roles: &[S],
180 ) -> ClResult<Setting> {
181 let def = self
183 .registry
184 .get(key)
185 .ok_or_else(|| Error::ValidationError(format!("Unknown setting: {}", 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
228 }
229 };
230
231 if let Some(default) = &def.default
233 && !value.matches_type(default)
234 {
235 return Err(Error::ValidationError(format!(
236 "Type mismatch for setting '{}': expected {}, got {}",
237 key,
238 default.type_name(),
239 value.type_name()
240 )));
241 }
242
243 if let Some(validator) = &def.validator {
245 validator(&value)?;
246 }
247
248 let json_value = serde_json::to_value(&value)
250 .map_err(|e| Error::ValidationError(format!("Failed to serialize setting: {}", e)))?;
251 self.meta.update_setting(storage_tn_id, key, Some(json_value)).await?;
252
253 self.cache.invalidate_key(key);
257
258 info!("Setting '{}' updated for tn_id={}", key, storage_tn_id.0);
259
260 Ok(Setting {
262 key: key.to_string(),
263 value,
264 tn_id: storage_tn_id,
265 updated_at: cloudillo_types::types::Timestamp::now(),
266 })
267 }
268
269 pub async fn delete(&self, tn_id: TnId, key: &str) -> ClResult<bool> {
271 self.meta.update_setting(tn_id, key, None).await?;
272 self.cache.invalidate_key(key);
273
274 info!("Setting '{}' deleted for tn_id={}", key, tn_id.0);
275 Ok(true)
276 }
277
278 pub async fn clear<S: AsRef<str>>(&self, tn_id: TnId, key: &str, roles: &[S]) -> ClResult<()> {
283 let def = self
284 .registry
285 .get(key)
286 .ok_or_else(|| Error::ValidationError(format!("Unknown setting: {}", key)))?;
287
288 if !def.permission.check(roles) {
289 warn!(
290 "Permission denied for clearing setting '{}': requires {:?}",
291 key, def.permission
292 );
293 return Err(Error::PermissionDenied);
294 }
295
296 let storage_tn_id = match (def.scope, tn_id.0) {
304 (SettingScope::System, _) => return Err(Error::PermissionDenied),
305 (SettingScope::Global | SettingScope::Tenant, 0) => {
306 if !roles.iter().any(|r| r.as_ref() == "SADM") {
310 return Err(Error::PermissionDenied);
311 }
312 TnId(0)
313 }
314 (SettingScope::Global, _) => {
315 if !roles.iter().any(|r| r.as_ref() == "SADM") {
316 return Err(Error::PermissionDenied);
317 }
318 TnId(0)
319 }
320 (SettingScope::Tenant, _) => tn_id,
321 };
322
323 self.meta.update_setting(storage_tn_id, key, None).await?;
324
325 self.cache.invalidate_key(key);
329
330 info!("Setting '{}' cleared for tn_id={}", key, storage_tn_id.0);
331 Ok(())
332 }
333
334 pub async fn validate_required_settings(&self) -> ClResult<()> {
336 for def in self.registry.list() {
337 if def.optional || def.default.is_some() {
339 continue;
340 }
341
342 if self.meta.read_setting(TnId(0), &def.key).await?.is_none() {
344 return Err(Error::ValidationError(format!(
345 "Required setting '{}' is not configured",
346 def.key
347 )));
348 }
349 }
350 Ok(())
351 }
352
353 pub async fn get_string(&self, tn_id: TnId, key: &str) -> ClResult<String> {
355 match self.get(tn_id, key).await? {
356 Some(SettingValue::String(s)) => Ok(s),
357 Some(v) => Err(Error::ValidationError(format!(
358 "Setting '{}' is not a string, got {}",
359 key,
360 v.type_name()
361 ))),
362 None => Err(Error::SettingNotFound(format!(
363 "Setting '{}' has no default and must be configured",
364 key
365 ))),
366 }
367 }
368
369 pub async fn get_int(&self, tn_id: TnId, key: &str) -> ClResult<i64> {
370 match self.get(tn_id, key).await? {
371 Some(SettingValue::Int(i)) => Ok(i),
372 Some(v) => Err(Error::ValidationError(format!(
373 "Setting '{}' is not an integer, got {}",
374 key,
375 v.type_name()
376 ))),
377 None => Err(Error::SettingNotFound(format!(
378 "Setting '{}' has no default and must be configured",
379 key
380 ))),
381 }
382 }
383
384 pub async fn get_bool(&self, tn_id: TnId, key: &str) -> ClResult<bool> {
385 match self.get(tn_id, key).await? {
386 Some(SettingValue::Bool(b)) => Ok(b),
387 Some(v) => Err(Error::ValidationError(format!(
388 "Setting '{}' is not a boolean, got {}",
389 key,
390 v.type_name()
391 ))),
392 None => Err(Error::SettingNotFound(format!(
393 "Setting '{}' has no default and must be configured",
394 key
395 ))),
396 }
397 }
398
399 pub async fn get_json(&self, tn_id: TnId, key: &str) -> ClResult<serde_json::Value> {
400 match self.get(tn_id, key).await? {
401 Some(SettingValue::Json(j)) => Ok(j),
402 Some(v) => Err(Error::ValidationError(format!(
403 "Setting '{}' is not JSON, got {}",
404 key,
405 v.type_name()
406 ))),
407 None => Err(Error::SettingNotFound(format!(
408 "Setting '{}' has no default and must be configured",
409 key
410 ))),
411 }
412 }
413
414 pub async fn get_string_opt(&self, tn_id: TnId, key: &str) -> ClResult<Option<String>> {
417 match self.get(tn_id, key).await {
418 Ok(Some(SettingValue::String(s))) => Ok(Some(s)),
419 Ok(Some(v)) => Err(Error::ValidationError(format!(
420 "Setting '{}' is not a string, got {}",
421 key,
422 v.type_name()
423 ))),
424 Ok(None) | Err(Error::SettingNotFound(_)) => Ok(None),
425 Err(e) => Err(e),
426 }
427 }
428
429 pub async fn get_int_opt(&self, tn_id: TnId, key: &str) -> ClResult<Option<i64>> {
430 match self.get(tn_id, key).await {
431 Ok(Some(SettingValue::Int(i))) => Ok(Some(i)),
432 Ok(Some(v)) => Err(Error::ValidationError(format!(
433 "Setting '{}' is not an integer, got {}",
434 key,
435 v.type_name()
436 ))),
437 Ok(None) | Err(Error::SettingNotFound(_)) => Ok(None),
438 Err(e) => Err(e),
439 }
440 }
441
442 pub async fn get_bool_opt(&self, tn_id: TnId, key: &str) -> ClResult<Option<bool>> {
443 match self.get(tn_id, key).await {
444 Ok(Some(SettingValue::Bool(b))) => Ok(Some(b)),
445 Ok(Some(v)) => Err(Error::ValidationError(format!(
446 "Setting '{}' is not a boolean, got {}",
447 key,
448 v.type_name()
449 ))),
450 Ok(None) | Err(Error::SettingNotFound(_)) => Ok(None),
451 Err(e) => Err(e),
452 }
453 }
454
455 pub async fn get_json_opt(
456 &self,
457 tn_id: TnId,
458 key: &str,
459 ) -> ClResult<Option<serde_json::Value>> {
460 match self.get(tn_id, key).await {
461 Ok(Some(SettingValue::Json(j))) => Ok(Some(j)),
462 Ok(Some(v)) => Err(Error::ValidationError(format!(
463 "Setting '{}' is not JSON, got {}",
464 key,
465 v.type_name()
466 ))),
467 Ok(None) | Err(Error::SettingNotFound(_)) => Ok(None),
468 Err(e) => Err(e),
469 }
470 }
471
472 pub fn registry(&self) -> &Arc<FrozenSettingsRegistry> {
474 &self.registry
475 }
476
477 pub async fn list_by_prefix(
483 &self,
484 tn_id: TnId,
485 prefix: &str,
486 ) -> ClResult<Vec<(String, SettingValue, &SettingDefinition)>> {
487 let prefixes = vec![format!("{}.", prefix)]; let global_settings = self.meta.list_settings(TnId(0), Some(&prefixes)).await?;
491
492 let tenant_settings = if tn_id.0 != 0 {
494 self.meta.list_settings(tn_id, Some(&prefixes)).await?
495 } else {
496 std::collections::HashMap::new()
497 };
498
499 let mut merged = global_settings;
501 merged.extend(tenant_settings);
502
503 let mut result = Vec::new();
504 for (key, json_value) in merged {
505 if let Some(definition) = self.registry.get(&key) {
506 let value = serde_json::from_value::<SettingValue>(json_value)
507 .map_err(|e| Error::ValidationError(format!("Invalid setting value: {}", e)))?;
508 result.push((key, value, definition));
509 }
510 }
511
512 Ok(result)
513 }
514}
515
516