Skip to main content

systemprompt_files/config/
validator.rs

1//! Domain validator for files configuration (size bounds, type sanity).
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use systemprompt_config::ProfileBootstrap;
7use systemprompt_models::AppPaths;
8use systemprompt_traits::validation_report::{
9    ValidationError, ValidationReport, ValidationWarning,
10};
11use systemprompt_traits::{ConfigProvider, DomainConfig, DomainConfigError};
12
13use super::FilesConfig;
14use super::types::FilesConfigYaml;
15
16const MAX_RECOMMENDED_FILE_SIZE: u64 = 2 * 1024 * 1024 * 1024;
17const MIN_VIDEO_FILE_SIZE: u64 = 100 * 1024 * 1024;
18
19#[derive(Debug, Default)]
20pub struct FilesConfigValidator {
21    config: Option<FilesConfigYaml>,
22}
23
24impl FilesConfigValidator {
25    pub fn new() -> Self {
26        Self::default()
27    }
28}
29
30impl DomainConfig for FilesConfigValidator {
31    fn domain_id(&self) -> &'static str {
32        "files"
33    }
34
35    fn priority(&self) -> u32 {
36        10
37    }
38
39    fn load(&mut self, _config: &dyn ConfigProvider) -> Result<(), DomainConfigError> {
40        let profile = ProfileBootstrap::get().map_err(|e| DomainConfigError::LoadError {
41            message: e.to_string(),
42        })?;
43        let paths =
44            AppPaths::from_profile(&profile.paths).map_err(|e| DomainConfigError::LoadError {
45                message: e.to_string(),
46            })?;
47        let yaml_config =
48            FilesConfig::load_yaml_config(&paths).map_err(|e| DomainConfigError::LoadError {
49                message: e.to_string(),
50            })?;
51        self.config = Some(yaml_config);
52        Ok(())
53    }
54
55    fn validate(&self) -> Result<ValidationReport, DomainConfigError> {
56        let mut report = ValidationReport::new("files");
57
58        let config = self
59            .config
60            .as_ref()
61            .ok_or_else(|| DomainConfigError::ValidationError {
62                message: "Not loaded".into(),
63            })?;
64
65        if !config.url_prefix.starts_with('/') {
66            report.add_error(ValidationError::new(
67                "files.urlPrefix",
68                "URL prefix must start with '/'",
69            ));
70        }
71
72        if config.upload.max_file_size_bytes > MAX_RECOMMENDED_FILE_SIZE {
73            report.add_warning(
74                ValidationWarning::new(
75                    "files.upload.maxFileSizeBytes",
76                    "Max file size > 2GB may cause memory issues",
77                )
78                .with_suggestion("Consider using a smaller max file size for better performance"),
79            );
80        }
81
82        if config.upload.allowed_types.video
83            && config.upload.max_file_size_bytes < MIN_VIDEO_FILE_SIZE
84        {
85            report.add_warning(
86                ValidationWarning::new(
87                    "files.upload.allowedTypes.video",
88                    "Video uploads enabled but max file size < 100MB",
89                )
90                .with_suggestion("Increase maxFileSizeBytes to at least 100MB for video uploads"),
91            );
92        }
93
94        Ok(report)
95    }
96}