1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Default, Serialize, Deserialize)]
6#[serde(default)]
7pub struct Config {
8 pub storage: StorageConfig,
9 pub search: SearchConfig,
10 pub validation: ValidationConfig,
11 pub privacy: PrivacyConfig,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15#[serde(default)]
16pub struct StorageConfig {
17 pub retention_days: u32,
18 pub vacuum_interval_secs: u64,
19 pub max_db_size_mb: u64,
20 pub busy_timeout_ms: u32,
21 pub cache_size_kb: u32,
22 pub dedup_window_secs: u64,
23 pub encryption_enabled: bool,
24}
25
26impl Default for StorageConfig {
27 fn default() -> Self {
28 Self {
29 retention_days: 90,
30 vacuum_interval_secs: 604800,
31 max_db_size_mb: 500,
32 busy_timeout_ms: 5000,
33 cache_size_kb: 2048,
34 dedup_window_secs: 900,
35 encryption_enabled: false,
36 }
37 }
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
41#[serde(default)]
42pub struct SearchConfig {
43 pub default_limit: u32,
44 pub max_limit: u32,
45 pub min_relevance_score: Option<f64>,
50 pub preview_max_chars: usize,
52 pub preview_min_chars: usize,
54 pub column_weights: BTreeMap<String, f64>,
57}
58
59fn default_column_weights() -> BTreeMap<String, f64> {
60 [
61 ("key".to_string(), 10.0),
62 ("value".to_string(), 1.0),
63 ("tags".to_string(), 5.0),
64 ("source_type".to_string(), 0.5),
65 ("scope".to_string(), 0.5),
66 ]
67 .into_iter()
68 .collect()
69}
70
71impl Default for SearchConfig {
72 fn default() -> Self {
73 Self {
74 default_limit: 10,
75 max_limit: 50,
76 min_relevance_score: None,
77 preview_max_chars: 400,
78 preview_min_chars: 80,
79 column_weights: default_column_weights(),
80 }
81 }
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
85#[serde(default)]
86pub struct ValidationConfig {
87 pub max_key_length: usize,
88 pub max_value_length: usize,
89 pub max_tags: usize,
90 pub max_tag_length: usize,
91}
92
93impl Default for ValidationConfig {
94 fn default() -> Self {
95 Self {
96 max_key_length: 256,
97 max_value_length: 2000,
98 max_tags: 20,
99 max_tag_length: 64,
100 }
101 }
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
105#[serde(default)]
106pub struct PrivacyConfig {
107 pub secret_patterns: Vec<String>,
108 pub extra_patterns: Vec<String>,
109 pub replace_defaults: bool,
110 pub file_deny_list: Vec<String>,
111}
112
113impl Default for PrivacyConfig {
114 fn default() -> Self {
115 Self {
116 secret_patterns: default_secret_patterns(),
117 extra_patterns: Vec::new(),
118 replace_defaults: false,
119 file_deny_list: vec![
120 ".env".into(),
121 ".env.*".into(),
122 "*.pem".into(),
123 "*.key".into(),
124 "*.p12".into(),
125 "*.pfx".into(),
126 "id_rsa".into(),
127 "id_ed25519".into(),
128 "id_ecdsa".into(),
129 "*.secret".into(),
130 "credentials.json".into(),
131 ],
132 }
133 }
134}
135
136pub fn default_secret_patterns() -> Vec<String> {
137 vec![
138 r"AKIA[0-9A-Z]{16}".into(),
139 r"-----BEGIN [A-Z ]*PRIVATE KEY-----".into(),
140 r"(?i)(api[_-]?key|token|secret|password)\s*[:=]\s*\S+".into(),
141 r"(?i)mongodb(\+srv)?://[^\s]+".into(),
142 r"(?i)postgres(ql)?://[^\s]+".into(),
143 r"(?i)mysql://[^\s]+".into(),
144 r"(?i)redis://[^\s]+".into(),
145 r"ghp_[a-zA-Z0-9]{36}".into(),
146 r"sk-[a-zA-Z0-9]{48}".into(),
147 r"xoxb-[0-9]+-[0-9]+-[a-zA-Z0-9]+".into(),
148 ]
149}