fraiseql_server/files/
config.rs1use std::collections::HashMap;
4
5use serde::Deserialize;
6
7#[derive(Debug, Clone, Deserialize)]
8pub struct FileConfig {
9 pub path: Option<String>,
11
12 #[serde(default = "default_allowed_types")]
14 pub allowed_types: Vec<String>,
15
16 #[serde(default = "default_max_size")]
18 pub max_size: String,
19
20 #[serde(default = "default_validate_magic")]
22 pub validate_magic_bytes: bool,
23
24 #[serde(default = "default_storage")]
26 pub storage: String,
27
28 pub bucket_env: Option<String>,
30
31 #[serde(default = "default_public")]
33 pub public: bool,
34
35 pub cache: Option<String>,
37
38 pub url_expiry: Option<String>,
40
41 #[serde(default)]
43 pub scan_malware: bool,
44
45 pub processing: Option<ProcessingConfig>,
47
48 pub on_upload: Option<UploadCallbackConfig>,
50}
51
52fn default_allowed_types() -> Vec<String> {
53 vec![
54 "image/jpeg".to_string(),
55 "image/png".to_string(),
56 "image/webp".to_string(),
57 "image/gif".to_string(),
58 "application/pdf".to_string(),
59 ]
60}
61
62fn default_max_size() -> String {
63 "10MB".to_string()
64}
65fn default_validate_magic() -> bool {
66 true
67}
68fn default_storage() -> String {
69 "default".to_string()
70}
71fn default_public() -> bool {
72 true
73}
74
75impl Default for FileConfig {
76 fn default() -> Self {
77 Self {
78 path: None,
79 allowed_types: default_allowed_types(),
80 max_size: default_max_size(),
81 validate_magic_bytes: default_validate_magic(),
82 storage: default_storage(),
83 bucket_env: None,
84 public: default_public(),
85 cache: None,
86 url_expiry: None,
87 scan_malware: false,
88 processing: None,
89 on_upload: None,
90 }
91 }
92}
93
94#[derive(Debug, Clone, Deserialize)]
95pub struct ProcessingConfig {
96 #[serde(default)]
98 pub strip_exif: bool,
99
100 pub output_format: Option<String>,
102
103 pub quality: Option<u8>,
105
106 #[serde(default)]
108 pub variants: Vec<VariantConfig>,
109}
110
111#[derive(Debug, Clone, Deserialize)]
112pub struct VariantConfig {
113 pub name: String,
114 pub width: u32,
115 pub height: u32,
116
117 #[serde(default = "default_mode")]
119 pub mode: String,
120}
121
122fn default_mode() -> String {
123 "fit".to_string()
124}
125
126#[derive(Debug, Clone, Deserialize)]
127pub struct UploadCallbackConfig {
128 pub function: String,
130
131 #[serde(default)]
133 pub mapping: HashMap<String, String>,
134}
135
136#[derive(Debug, Clone, Deserialize)]
137pub struct StorageConfig {
138 pub backend: String,
140
141 pub region: Option<String>,
143
144 pub bucket_env: Option<String>,
146
147 pub access_key_env: Option<String>,
149
150 pub secret_key_env: Option<String>,
152
153 pub endpoint_env: Option<String>,
155
156 pub account_id_env: Option<String>,
158
159 pub project_id_env: Option<String>,
161
162 pub credentials_env: Option<String>,
164
165 pub base_path: Option<String>,
167
168 pub serve_path: Option<String>,
170
171 pub public_url: Option<String>,
173}
174
175pub fn parse_size(size_str: &str) -> Result<usize, String> {
177 let size_str = size_str.trim().to_uppercase();
178
179 let (num_part, unit) =
180 if let Some(pos) = size_str.find(|c: char| !c.is_ascii_digit() && c != '.') {
181 (&size_str[..pos], &size_str[pos..])
182 } else {
183 (size_str.as_str(), "")
184 };
185
186 let num: f64 = num_part.parse().map_err(|_| format!("Invalid number: {}", num_part))?;
187
188 let multiplier = match unit.trim() {
189 "" | "B" => 1,
190 "KB" => 1024,
191 "MB" => 1024 * 1024,
192 "GB" => 1024 * 1024 * 1024,
193 _ => return Err(format!("Unknown unit: {}", unit)),
194 };
195
196 Ok((num * multiplier as f64) as usize)
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn test_parse_size() {
205 assert_eq!(parse_size("100").unwrap(), 100);
206 assert_eq!(parse_size("100B").unwrap(), 100);
207 assert_eq!(parse_size("10KB").unwrap(), 10 * 1024);
208 assert_eq!(parse_size("10MB").unwrap(), 10 * 1024 * 1024);
209 assert_eq!(parse_size("1GB").unwrap(), 1024 * 1024 * 1024);
210 }
211}