diff --git a/src/services/api/models/agent/status.rs b/src/services/api/models/agent/status.rs index 34557ac..293da97 100644 --- a/src/services/api/models/agent/status.rs +++ b/src/services/api/models/agent/status.rs @@ -24,6 +24,8 @@ pub struct DatabaseStorage { #[serde(deserialize_with = "deserialize_snake_case")] pub config: Value, pub provider: String, + #[serde(default)] + pub prefix: Option, } #[derive(Debug, Deserialize)] diff --git a/src/services/storage/providers/azure_blob/mod.rs b/src/services/storage/providers/azure_blob/mod.rs index b0b5e6b..9a06481 100644 --- a/src/services/storage/providers/azure_blob/mod.rs +++ b/src/services/storage/providers/azure_blob/mod.rs @@ -95,7 +95,7 @@ impl StorageProvider for AzureBlobProvider { }; let file_name = full_file_name(encrypt); - let remote_file_path = full_file_path(&file_name); + let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); info!( "Starting block upload to azure blob {}/{}", config.container_name, remote_file_path diff --git a/src/services/storage/providers/google_cloud_storage/mod.rs b/src/services/storage/providers/google_cloud_storage/mod.rs index 453a63e..997a973 100644 --- a/src/services/storage/providers/google_cloud_storage/mod.rs +++ b/src/services/storage/providers/google_cloud_storage/mod.rs @@ -85,7 +85,7 @@ impl StorageProvider for GoogleCloudStorageProvider { let file_name = full_file_name(encrypt); info!("Uploading file {}", file_name); - let remote_file_path = full_file_path(&file_name); + let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); let client = match build_client(&config).await { Ok(c) => c, diff --git a/src/services/storage/providers/google_drive/mod.rs b/src/services/storage/providers/google_drive/mod.rs index 89b11b5..25ab40e 100644 --- a/src/services/storage/providers/google_drive/mod.rs +++ b/src/services/storage/providers/google_drive/mod.rs @@ -85,7 +85,7 @@ impl StorageProvider for GoogleDriveProvider { info!("Uploading file {}", file_name); - let remote_file_path = full_file_path(&file_name); + let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); match upload_stream_to_google_drive( &config, diff --git a/src/services/storage/providers/local.rs b/src/services/storage/providers/local.rs index a574faa..2dd4e5b 100644 --- a/src/services/storage/providers/local.rs +++ b/src/services/storage/providers/local.rs @@ -38,7 +38,7 @@ impl StorageProvider for LocalProvider { let encrypt = encrypt.unwrap_or(false); let file_name = full_file_name(encrypt); - let remote_file_path = full_file_path(&file_name); + let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); let total_size = match fs::metadata(&file_path).await { Ok(meta) => meta.len(), diff --git a/src/services/storage/providers/s3/mod.rs b/src/services/storage/providers/s3/mod.rs index f01db64..6c85f5f 100644 --- a/src/services/storage/providers/s3/mod.rs +++ b/src/services/storage/providers/s3/mod.rs @@ -148,7 +148,7 @@ impl StorageProvider for S3Provider { info!("Uploading file {}", file_name); let bucket = &config.bucket_name; - let remote_file_path = full_file_path(&file_name); + let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); info!("S3 key {:}", remote_file_path); info!( "Starting multipart upload to s3://{}/{}", diff --git a/src/tests/utils/file_tests.rs b/src/tests/utils/file_tests.rs index 697a5d5..5acb43a 100644 --- a/src/tests/utils/file_tests.rs +++ b/src/tests/utils/file_tests.rs @@ -30,12 +30,16 @@ fn full_file_name_matches_expected_suffix() { } #[test] -fn full_file_path_prefixes_backups_directory_and_date() { +fn full_file_path_uses_default_or_configured_prefix() { let file_name = "backup.tar.gz".to_string(); - let full_path = full_file_path(&file_name); - assert!(full_path.starts_with("backups/")); - assert!(full_path.ends_with("/backup.tar.gz")); + let default_path = full_file_path(&file_name, None); + assert!(default_path.starts_with("backups/")); + assert!(default_path.ends_with("/backup.tar.gz")); + + let configured_path = full_file_path(&file_name, Some("/portabase/")); + assert!(configured_path.starts_with("portabase/")); + assert!(configured_path.ends_with("/backup.tar.gz")); } #[tokio::test] diff --git a/src/utils/file.rs b/src/utils/file.rs index 13f0ea2..0949afc 100644 --- a/src/utils/file.rs +++ b/src/utils/file.rs @@ -47,8 +47,14 @@ pub fn full_file_name(encrypt: bool) -> String { } } -pub fn full_file_path(file_name: &String) -> String { - format!("backups/{}/{}", Utc::now().format("%Y-%m-%d"), file_name) +pub fn full_file_path(file_name: &String, prefix: Option<&str>) -> String { + let prefix = prefix + .map(str::trim) + .map(|prefix| prefix.trim_matches(char::from(47))) + .filter(|prefix| !prefix.is_empty()) + .unwrap_or("backups"); + + format!("{}/{}/{}", prefix, Utc::now().format("%Y-%m-%d"), file_name) } const CHUNK_SIZE: usize = 16 * 1024 * 1024;