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