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)]
438#[serde(deny_unknown_fields, rename_all = "camelCase")]
439pub struct WebdavDocument {
440 #[serde(default, deserialize_with = "present")]
441 pub enabled: Option<bool>,
442 #[serde(default, deserialize_with = "present")]
443 pub read: Option<bool>,
444 #[serde(default, deserialize_with = "present")]
445 pub write: Option<bool>,
446 #[serde(default, deserialize_with = "present")]
447 pub endpoint: Option<String>,
448 #[serde(default, deserialize_with = "present")]
449 pub username: Option<String>,
450 #[serde(default, deserialize_with = "present")]
451 pub bucket: Option<String>,
452 pub password: MaskedSecret,
453 pub password_set: bool,
454}
455#[derive(Clone, Debug, Default, Deserialize, Serialize)]
456#[serde(deny_unknown_fields, rename_all = "camelCase")]
457pub struct WebdavUpdate {
458 #[serde(
459 default,
460 deserialize_with = "present",
461 skip_serializing_if = "Option::is_none"
462 )]
463 pub enabled: Option<bool>,
464 #[serde(
465 default,
466 deserialize_with = "present",
467 skip_serializing_if = "Option::is_none"
468 )]
469 pub read: Option<bool>,
470 #[serde(
471 default,
472 deserialize_with = "present",
473 skip_serializing_if = "Option::is_none"
474 )]
475 pub write: Option<bool>,
476 #[serde(
477 default,
478 deserialize_with = "present",
479 skip_serializing_if = "Option::is_none"
480 )]
481 pub endpoint: Option<String>,
482 #[serde(
483 default,
484 deserialize_with = "present",
485 skip_serializing_if = "Option::is_none"
486 )]
487 pub username: Option<String>,
488 #[serde(
489 default,
490 deserialize_with = "present",
491 skip_serializing_if = "Option::is_none"
492 )]
493 pub bucket: Option<String>,
494 #[serde(skip_serializing_if = "Option::is_none")]
495 pub password: Option<StorageSecret>,
496}
497
498#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
499#[serde(deny_unknown_fields, rename_all = "camelCase")]
500pub struct StorageConfigDocument {
501 #[serde(default, deserialize_with = "present_object")]
502 pub webdav: Option<WebdavDocument>,
503 #[serde(default, deserialize_with = "present_object")]
504 pub filesystem: Option<FilesystemDocument>,
505 #[serde(default, deserialize_with = "present_object")]
506 pub gridfs: Option<GridfsDocument>,
507 #[serde(default, deserialize_with = "present_object")]
508 pub s3: Option<S3Document>,
509 #[serde(default, deserialize_with = "present_object")]
510 pub azure: Option<AzureDocument>,
511 #[serde(default, deserialize_with = "present_object")]
512 pub gcs: Option<GcsDocument>,
513}
514
515#[derive(Clone, Debug, Default, Deserialize, Serialize)]
516#[serde(deny_unknown_fields, rename_all = "camelCase")]
517pub struct StorageConfigUpdate {
518 #[serde(
519 default,
520 deserialize_with = "present_object",
521 skip_serializing_if = "Option::is_none"
522 )]
523 pub webdav: Option<WebdavUpdate>,
524 #[serde(
525 default,
526 deserialize_with = "present_object",
527 skip_serializing_if = "Option::is_none"
528 )]
529 pub filesystem: Option<FilesystemUpdate>,
530 #[serde(
531 default,
532 deserialize_with = "present_object",
533 skip_serializing_if = "Option::is_none"
534 )]
535 pub gridfs: Option<GridfsUpdate>,
536 #[serde(
537 default,
538 deserialize_with = "present_object",
539 skip_serializing_if = "Option::is_none"
540 )]
541 pub s3: Option<S3Update>,
542 #[serde(
543 default,
544 deserialize_with = "present_object",
545 skip_serializing_if = "Option::is_none"
546 )]
547 pub azure: Option<AzureUpdate>,
548 #[serde(
549 default,
550 deserialize_with = "present_object",
551 skip_serializing_if = "Option::is_none"
552 )]
553 pub gcs: Option<GcsUpdate>,
554}
555
556#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
557#[serde(rename_all = "lowercase")]
558pub enum StorageBackend {
559 #[default]
560 Fs,
561 Gridfs,
562 S3,
563 Azure,
564 Gcs,
565 Webdav,
566}
567#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
568#[serde(deny_unknown_fields, rename_all = "camelCase")]
569pub struct AttachmentSettingsDocument {
570 pub default_storage: StorageBackend,
571 #[serde(default, deserialize_with = "nullable_object")]
572 pub storage_config: Option<StorageConfigDocument>,
573 #[serde(default, deserialize_with = "nullable_object")]
574 pub upload_settings: Option<UploadDocument>,
575 #[serde(default, deserialize_with = "nullable_object")]
576 pub limit_settings: Option<LimitsDocument>,
577 #[serde(default, deserialize_with = "nullable_object")]
578 pub migration_settings: Option<MigrationDocument>,
579 #[serde(deserialize_with = "super::date")]
580 pub created_at: String,
581 #[serde(deserialize_with = "super::date")]
582 pub updated_at: String,
583 #[serde(default)]
584 pub created_by: Option<String>,
585 #[serde(default)]
586 pub updated_by: Option<String>,
587 #[serde(rename = "_id")]
588 pub settings_id: String,
589}
590
591#[derive(Clone, Debug, Default, Deserialize, Serialize)]
592#[serde(deny_unknown_fields, rename_all = "camelCase")]
593pub struct UpdateAttachmentSettings {
594 #[serde(
595 default,
596 deserialize_with = "present",
597 skip_serializing_if = "Option::is_none"
598 )]
599 pub default_storage: Option<StorageBackend>,
600 #[serde(
601 default,
602 deserialize_with = "present_object",
603 skip_serializing_if = "Option::is_none"
604 )]
605 pub storage_config: Option<StorageConfigUpdate>,
606 #[serde(
607 default,
608 deserialize_with = "present_object",
609 skip_serializing_if = "Option::is_none"
610 )]
611 pub upload_settings: Option<UploadUpdate>,
612 #[serde(
613 default,
614 deserialize_with = "present_object",
615 skip_serializing_if = "Option::is_none"
616 )]
617 pub limit_settings: Option<LimitsUpdate>,
618 #[serde(
619 default,
620 deserialize_with = "present_object",
621 skip_serializing_if = "Option::is_none"
622 )]
623 pub migration_settings: Option<MigrationUpdate>,
624}
625
626impl std::str::FromStr for StorageBackend {
627 type Err = &'static str;
628 fn from_str(value: &str) -> Result<Self, Self::Err> {
629 match value {
630 "fs" => Ok(Self::Fs),
631 "gridfs" => Ok(Self::Gridfs),
632 "s3" => Ok(Self::S3),
633 "azure" => Ok(Self::Azure),
634 "gcs" => Ok(Self::Gcs),
635 "webdav" => Ok(Self::Webdav),
636 _ => Err("expected fs, gridfs, s3, azure, gcs, or webdav"),
637 }
638 }
639}
640
641impl UpdateAttachmentSettings {
642 pub(crate) fn redaction_secrets(&self) -> Vec<secrecy::SecretString> {
643 let mut secrets = Vec::new();
644 if let Some(config) = &self.storage_config {
645 if let Some(secret) = config
646 .webdav
647 .as_ref()
648 .and_then(|provider| provider.password.as_ref())
649 {
650 secrets.push(secret.0.clone());
651 }
652 if let Some(provider) = &config.s3 {
653 if let Some(secret) = &provider.secret_access_key {
654 secrets.push(secret.0.clone());
655 }
656 }
657 if let Some(provider) = &config.azure {
658 if let Some(secret) = &provider.account_key {
659 secrets.push(secret.0.clone());
660 }
661 if let Some(secret) = &provider.connection_string {
662 secrets.push(secret.0.clone());
663 }
664 }
665 if let Some(provider) = &config.gcs {
666 if let Some(secret) = &provider.credentials {
667 secrets.extend(secret.redaction_secrets());
668 }
669 }
670 }
671 secrets
672 }
673}