1use super::{
2 BatchSize, CpuThreshold, DelayMs, GcsCredentials, MaskedSecret, NonNegativeNumber,
3 SettingNumber, StorageSecret, nullable_object, present, present_object,
4};
5use serde::{Deserialize, Serialize};
6#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
7#[serde(deny_unknown_fields, rename_all = "camelCase")]
8pub struct FilesystemDocument {
9 pub enabled: bool,
10 pub read: bool,
11 pub write: bool,
12 #[serde(default, deserialize_with = "present")]
13 pub path: Option<String>,
14}
15
16#[derive(Clone, Debug, Default, Deserialize, Serialize)]
17#[serde(deny_unknown_fields, rename_all = "camelCase")]
18pub struct FilesystemUpdate {
19 #[serde(
20 default,
21 deserialize_with = "present",
22 skip_serializing_if = "Option::is_none"
23 )]
24 pub enabled: Option<bool>,
25 #[serde(
26 default,
27 deserialize_with = "present",
28 skip_serializing_if = "Option::is_none"
29 )]
30 pub read: Option<bool>,
31 #[serde(
32 default,
33 deserialize_with = "present",
34 skip_serializing_if = "Option::is_none"
35 )]
36 pub write: Option<bool>,
37 #[serde(
38 default,
39 deserialize_with = "present",
40 skip_serializing_if = "Option::is_none"
41 )]
42 pub path: Option<String>,
43}
44
45#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
46#[serde(deny_unknown_fields, rename_all = "camelCase")]
47pub struct GridfsDocument {
48 pub enabled: bool,
49 pub read: bool,
50 pub write: bool,
51}
52
53#[derive(Clone, Debug, Default, Deserialize, Serialize)]
54#[serde(deny_unknown_fields, rename_all = "camelCase")]
55pub struct GridfsUpdate {
56 #[serde(
57 default,
58 deserialize_with = "present",
59 skip_serializing_if = "Option::is_none"
60 )]
61 pub enabled: Option<bool>,
62 #[serde(
63 default,
64 deserialize_with = "present",
65 skip_serializing_if = "Option::is_none"
66 )]
67 pub read: Option<bool>,
68 #[serde(
69 default,
70 deserialize_with = "present",
71 skip_serializing_if = "Option::is_none"
72 )]
73 pub write: Option<bool>,
74}
75
76#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
77#[serde(deny_unknown_fields, rename_all = "camelCase")]
78pub struct S3Document {
79 #[serde(default, deserialize_with = "present")]
80 pub enabled: Option<bool>,
81 #[serde(default, deserialize_with = "present")]
82 pub read: Option<bool>,
83 #[serde(default, deserialize_with = "present")]
84 pub write: Option<bool>,
85 #[serde(default, deserialize_with = "present")]
86 pub endpoint: Option<String>,
87 #[serde(default, deserialize_with = "present")]
88 pub region: Option<String>,
89 #[serde(default, deserialize_with = "present")]
90 pub bucket: Option<String>,
91 #[serde(default, deserialize_with = "present")]
92 pub access_key_id: Option<String>,
93 #[serde(default, deserialize_with = "present")]
94 pub force_path_style: Option<bool>,
95 pub secret_access_key: MaskedSecret,
96 pub secret_access_key_set: bool,
97}
98
99#[derive(Clone, Debug, Default, Deserialize, Serialize)]
100#[serde(deny_unknown_fields, rename_all = "camelCase")]
101pub struct S3Update {
102 #[serde(
103 default,
104 deserialize_with = "present",
105 skip_serializing_if = "Option::is_none"
106 )]
107 pub enabled: Option<bool>,
108 #[serde(
109 default,
110 deserialize_with = "present",
111 skip_serializing_if = "Option::is_none"
112 )]
113 pub read: Option<bool>,
114 #[serde(
115 default,
116 deserialize_with = "present",
117 skip_serializing_if = "Option::is_none"
118 )]
119 pub write: Option<bool>,
120 #[serde(
121 default,
122 deserialize_with = "present",
123 skip_serializing_if = "Option::is_none"
124 )]
125 pub endpoint: Option<String>,
126 #[serde(
127 default,
128 deserialize_with = "present",
129 skip_serializing_if = "Option::is_none"
130 )]
131 pub region: Option<String>,
132 #[serde(
133 default,
134 deserialize_with = "present",
135 skip_serializing_if = "Option::is_none"
136 )]
137 pub bucket: Option<String>,
138 #[serde(
139 default,
140 deserialize_with = "present",
141 skip_serializing_if = "Option::is_none"
142 )]
143 pub access_key_id: Option<String>,
144 #[serde(
145 default,
146 deserialize_with = "present",
147 skip_serializing_if = "Option::is_none"
148 )]
149 pub force_path_style: Option<bool>,
150 #[serde(
151 default,
152 deserialize_with = "present",
153 skip_serializing_if = "Option::is_none"
154 )]
155 pub secret_access_key: Option<StorageSecret>,
156}
157
158#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
159#[serde(deny_unknown_fields, rename_all = "camelCase")]
160pub struct AzureDocument {
161 #[serde(default, deserialize_with = "present")]
162 pub enabled: Option<bool>,
163 #[serde(default, deserialize_with = "present")]
164 pub read: Option<bool>,
165 #[serde(default, deserialize_with = "present")]
166 pub write: Option<bool>,
167 #[serde(default, deserialize_with = "present")]
168 pub account_name: Option<String>,
169 #[serde(default, deserialize_with = "present")]
170 pub bucket: Option<String>,
171 pub account_key: MaskedSecret,
172 pub account_key_set: bool,
173 pub connection_string: MaskedSecret,
174 pub connection_string_set: bool,
175}
176
177#[derive(Clone, Debug, Default, Deserialize, Serialize)]
178#[serde(deny_unknown_fields, rename_all = "camelCase")]
179pub struct AzureUpdate {
180 #[serde(
181 default,
182 deserialize_with = "present",
183 skip_serializing_if = "Option::is_none"
184 )]
185 pub enabled: Option<bool>,
186 #[serde(
187 default,
188 deserialize_with = "present",
189 skip_serializing_if = "Option::is_none"
190 )]
191 pub read: Option<bool>,
192 #[serde(
193 default,
194 deserialize_with = "present",
195 skip_serializing_if = "Option::is_none"
196 )]
197 pub write: Option<bool>,
198 #[serde(
199 default,
200 deserialize_with = "present",
201 skip_serializing_if = "Option::is_none"
202 )]
203 pub account_name: Option<String>,
204 #[serde(
205 default,
206 deserialize_with = "present",
207 skip_serializing_if = "Option::is_none"
208 )]
209 pub bucket: Option<String>,
210 #[serde(
211 default,
212 deserialize_with = "present",
213 skip_serializing_if = "Option::is_none"
214 )]
215 pub account_key: Option<StorageSecret>,
216 #[serde(
217 default,
218 deserialize_with = "present",
219 skip_serializing_if = "Option::is_none"
220 )]
221 pub connection_string: Option<StorageSecret>,
222}
223
224#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
225#[serde(deny_unknown_fields, rename_all = "camelCase")]
226pub struct GcsDocument {
227 #[serde(default, deserialize_with = "present")]
228 pub enabled: Option<bool>,
229 #[serde(default, deserialize_with = "present")]
230 pub read: Option<bool>,
231 #[serde(default, deserialize_with = "present")]
232 pub write: Option<bool>,
233 #[serde(default, deserialize_with = "present")]
234 pub project_id: Option<String>,
235 #[serde(default, deserialize_with = "present")]
236 pub key_filename: Option<String>,
237 #[serde(default, deserialize_with = "present")]
238 pub bucket: Option<String>,
239 pub credentials: MaskedSecret,
240 pub credentials_set: bool,
241}
242
243#[derive(Clone, Debug, Default, Deserialize, Serialize)]
244#[serde(deny_unknown_fields, rename_all = "camelCase")]
245pub struct GcsUpdate {
246 #[serde(
247 default,
248 deserialize_with = "present",
249 skip_serializing_if = "Option::is_none"
250 )]
251 pub enabled: Option<bool>,
252 #[serde(
253 default,
254 deserialize_with = "present",
255 skip_serializing_if = "Option::is_none"
256 )]
257 pub read: Option<bool>,
258 #[serde(
259 default,
260 deserialize_with = "present",
261 skip_serializing_if = "Option::is_none"
262 )]
263 pub write: Option<bool>,
264 #[serde(
265 default,
266 deserialize_with = "present",
267 skip_serializing_if = "Option::is_none"
268 )]
269 pub project_id: Option<String>,
270 #[serde(
271 default,
272 deserialize_with = "present",
273 skip_serializing_if = "Option::is_none"
274 )]
275 pub key_filename: Option<String>,
276 #[serde(
277 default,
278 deserialize_with = "present",
279 skip_serializing_if = "Option::is_none"
280 )]
281 pub bucket: Option<String>,
282 #[serde(
283 default,
284 deserialize_with = "present",
285 skip_serializing_if = "Option::is_none"
286 )]
287 pub credentials: Option<GcsCredentials>,
288}
289
290#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
291#[serde(deny_unknown_fields, rename_all = "camelCase")]
292pub struct UploadDocument {
293 #[serde(default, deserialize_with = "present")]
294 pub max_file_size: Option<SettingNumber>,
295 #[serde(default, deserialize_with = "present")]
296 pub allowed_mime_types: Option<Vec<String>>,
297}
298
299#[derive(Clone, Debug, Default, Deserialize, Serialize)]
300#[serde(deny_unknown_fields, rename_all = "camelCase")]
301pub struct UploadUpdate {
302 #[serde(
303 default,
304 deserialize_with = "present",
305 skip_serializing_if = "Option::is_none"
306 )]
307 pub max_file_size: Option<SettingNumber>,
308 #[serde(
309 default,
310 deserialize_with = "present",
311 skip_serializing_if = "Option::is_none"
312 )]
313 pub allowed_mime_types: Option<Vec<String>>,
314}
315
316#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
317#[serde(deny_unknown_fields, rename_all = "camelCase")]
318pub struct LimitsDocument {
319 #[serde(default, deserialize_with = "present")]
320 pub attachments_upload_max_bytes: Option<NonNegativeNumber>,
321 #[serde(default, deserialize_with = "present")]
322 pub attachments_upload_blocked: Option<bool>,
323 #[serde(default, deserialize_with = "present")]
324 pub avatars_upload_blocked: Option<bool>,
325 #[serde(default, deserialize_with = "present")]
326 pub attachments_download_max_bytes: Option<NonNegativeNumber>,
327 #[serde(default, deserialize_with = "present")]
328 pub attachments_download_blocked: Option<bool>,
329 #[serde(default, deserialize_with = "present")]
330 pub api_upload_max_bytes: Option<NonNegativeNumber>,
331 #[serde(default, deserialize_with = "present")]
332 pub api_upload_blocked: Option<bool>,
333 #[serde(default, deserialize_with = "present")]
334 pub api_download_max_bytes: Option<NonNegativeNumber>,
335 #[serde(default, deserialize_with = "present")]
336 pub api_download_blocked: Option<bool>,
337}
338
339#[derive(Clone, Debug, Default, Deserialize, Serialize)]
340#[serde(deny_unknown_fields, rename_all = "camelCase")]
341pub struct LimitsUpdate {
342 #[serde(
343 default,
344 deserialize_with = "present",
345 skip_serializing_if = "Option::is_none"
346 )]
347 pub attachments_upload_max_bytes: Option<NonNegativeNumber>,
348 #[serde(
349 default,
350 deserialize_with = "present",
351 skip_serializing_if = "Option::is_none"
352 )]
353 pub attachments_upload_blocked: Option<bool>,
354 #[serde(
355 default,
356 deserialize_with = "present",
357 skip_serializing_if = "Option::is_none"
358 )]
359 pub avatars_upload_blocked: Option<bool>,
360 #[serde(
361 default,
362 deserialize_with = "present",
363 skip_serializing_if = "Option::is_none"
364 )]
365 pub attachments_download_max_bytes: Option<NonNegativeNumber>,
366 #[serde(
367 default,
368 deserialize_with = "present",
369 skip_serializing_if = "Option::is_none"
370 )]
371 pub attachments_download_blocked: Option<bool>,
372 #[serde(
373 default,
374 deserialize_with = "present",
375 skip_serializing_if = "Option::is_none"
376 )]
377 pub api_upload_max_bytes: Option<NonNegativeNumber>,
378 #[serde(
379 default,
380 deserialize_with = "present",
381 skip_serializing_if = "Option::is_none"
382 )]
383 pub api_upload_blocked: Option<bool>,
384 #[serde(
385 default,
386 deserialize_with = "present",
387 skip_serializing_if = "Option::is_none"
388 )]
389 pub api_download_max_bytes: Option<NonNegativeNumber>,
390 #[serde(
391 default,
392 deserialize_with = "present",
393 skip_serializing_if = "Option::is_none"
394 )]
395 pub api_download_blocked: Option<bool>,
396}
397
398#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
399#[serde(deny_unknown_fields, rename_all = "camelCase")]
400pub struct MigrationDocument {
401 pub auto_migrate: bool,
402 pub batch_size: BatchSize,
403 pub delay_ms: DelayMs,
404 pub cpu_threshold: CpuThreshold,
405}
406
407#[derive(Clone, Debug, Default, Deserialize, Serialize)]
408#[serde(deny_unknown_fields, rename_all = "camelCase")]
409pub struct MigrationUpdate {
410 #[serde(
411 default,
412 deserialize_with = "present",
413 skip_serializing_if = "Option::is_none"
414 )]
415 pub auto_migrate: Option<bool>,
416 #[serde(
417 default,
418 deserialize_with = "present",
419 skip_serializing_if = "Option::is_none"
420 )]
421 pub batch_size: Option<BatchSize>,
422 #[serde(
423 default,
424 deserialize_with = "present",
425 skip_serializing_if = "Option::is_none"
426 )]
427 pub delay_ms: Option<DelayMs>,
428 #[serde(
429 default,
430 deserialize_with = "present",
431 skip_serializing_if = "Option::is_none"
432 )]
433 pub cpu_threshold: Option<CpuThreshold>,
434}
435
436#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
437#[serde(deny_unknown_fields, rename_all = "camelCase")]
438pub struct StorageConfigDocument {
439 #[serde(default, deserialize_with = "present_object")]
440 pub filesystem: Option<FilesystemDocument>,
441 #[serde(default, deserialize_with = "present_object")]
442 pub gridfs: Option<GridfsDocument>,
443 #[serde(default, deserialize_with = "present_object")]
444 pub s3: Option<S3Document>,
445 #[serde(default, deserialize_with = "present_object")]
446 pub azure: Option<AzureDocument>,
447 #[serde(default, deserialize_with = "present_object")]
448 pub gcs: Option<GcsDocument>,
449}
450
451#[derive(Clone, Debug, Default, Deserialize, Serialize)]
452#[serde(deny_unknown_fields, rename_all = "camelCase")]
453pub struct StorageConfigUpdate {
454 #[serde(
455 default,
456 deserialize_with = "present_object",
457 skip_serializing_if = "Option::is_none"
458 )]
459 pub filesystem: Option<FilesystemUpdate>,
460 #[serde(
461 default,
462 deserialize_with = "present_object",
463 skip_serializing_if = "Option::is_none"
464 )]
465 pub gridfs: Option<GridfsUpdate>,
466 #[serde(
467 default,
468 deserialize_with = "present_object",
469 skip_serializing_if = "Option::is_none"
470 )]
471 pub s3: Option<S3Update>,
472 #[serde(
473 default,
474 deserialize_with = "present_object",
475 skip_serializing_if = "Option::is_none"
476 )]
477 pub azure: Option<AzureUpdate>,
478 #[serde(
479 default,
480 deserialize_with = "present_object",
481 skip_serializing_if = "Option::is_none"
482 )]
483 pub gcs: Option<GcsUpdate>,
484}
485
486#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
487#[serde(rename_all = "lowercase")]
488pub enum StorageBackend {
489 #[default]
490 Fs,
491 Gridfs,
492 S3,
493 Azure,
494 Gcs,
495}
496#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
497#[serde(deny_unknown_fields, rename_all = "camelCase")]
498pub struct AttachmentSettingsDocument {
499 pub default_storage: StorageBackend,
500 #[serde(default, deserialize_with = "nullable_object")]
501 pub storage_config: Option<StorageConfigDocument>,
502 #[serde(default, deserialize_with = "nullable_object")]
503 pub upload_settings: Option<UploadDocument>,
504 #[serde(default, deserialize_with = "nullable_object")]
505 pub limit_settings: Option<LimitsDocument>,
506 #[serde(default, deserialize_with = "nullable_object")]
507 pub migration_settings: Option<MigrationDocument>,
508 #[serde(deserialize_with = "super::date")]
509 pub created_at: String,
510 #[serde(deserialize_with = "super::date")]
511 pub updated_at: String,
512 #[serde(default)]
513 pub created_by: Option<String>,
514 #[serde(default)]
515 pub updated_by: Option<String>,
516 #[serde(rename = "_id")]
517 pub settings_id: String,
518}
519
520#[derive(Clone, Debug, Default, Deserialize, Serialize)]
521#[serde(deny_unknown_fields, rename_all = "camelCase")]
522pub struct UpdateAttachmentSettings {
523 #[serde(
524 default,
525 deserialize_with = "present",
526 skip_serializing_if = "Option::is_none"
527 )]
528 pub default_storage: Option<StorageBackend>,
529 #[serde(
530 default,
531 deserialize_with = "present_object",
532 skip_serializing_if = "Option::is_none"
533 )]
534 pub storage_config: Option<StorageConfigUpdate>,
535 #[serde(
536 default,
537 deserialize_with = "present_object",
538 skip_serializing_if = "Option::is_none"
539 )]
540 pub upload_settings: Option<UploadUpdate>,
541 #[serde(
542 default,
543 deserialize_with = "present_object",
544 skip_serializing_if = "Option::is_none"
545 )]
546 pub limit_settings: Option<LimitsUpdate>,
547 #[serde(
548 default,
549 deserialize_with = "present_object",
550 skip_serializing_if = "Option::is_none"
551 )]
552 pub migration_settings: Option<MigrationUpdate>,
553}
554
555impl std::str::FromStr for StorageBackend {
556 type Err = &'static str;
557 fn from_str(value: &str) -> Result<Self, Self::Err> {
558 match value {
559 "fs" => Ok(Self::Fs),
560 "gridfs" => Ok(Self::Gridfs),
561 "s3" => Ok(Self::S3),
562 "azure" => Ok(Self::Azure),
563 "gcs" => Ok(Self::Gcs),
564 _ => Err("expected fs, gridfs, s3, azure, or gcs"),
565 }
566 }
567}
568
569impl UpdateAttachmentSettings {
570 pub(crate) fn redaction_secrets(&self) -> Vec<secrecy::SecretString> {
571 let mut secrets = Vec::new();
572 if let Some(config) = &self.storage_config {
573 if let Some(provider) = &config.s3 {
574 if let Some(secret) = &provider.secret_access_key {
575 secrets.push(secret.0.clone());
576 }
577 }
578 if let Some(provider) = &config.azure {
579 if let Some(secret) = &provider.account_key {
580 secrets.push(secret.0.clone());
581 }
582 if let Some(secret) = &provider.connection_string {
583 secrets.push(secret.0.clone());
584 }
585 }
586 if let Some(provider) = &config.gcs {
587 if let Some(secret) = &provider.credentials {
588 secrets.extend(secret.redaction_secrets());
589 }
590 }
591 }
592 secrets
593 }
594}