use serde::{Deserialize, Serialize};
use zeroize::{Zeroize, ZeroizeOnDrop};
pub use cloud_sync_core::SyncMode;
#[derive(Debug, Clone, Serialize, Deserialize, Default, Zeroize, ZeroizeOnDrop)]
pub struct CommonProviderSettings {
pub destination_folder: Option<String>,
pub enabled: Option<bool>,
#[zeroize(skip)]
pub sync_mode: Option<SyncMode>,
pub encryption_password: Option<String>,
pub max_upload_rate: Option<u64>,
pub max_download_rate: Option<u64>,
#[zeroize(skip)]
pub selective_sync: Option<Vec<String>>,
}
pub trait ProviderConfig {
fn common_settings(&self) -> &CommonProviderSettings;
fn is_enabled(&self) -> bool {
self.common_settings().enabled.unwrap_or(true)
}
fn sync_mode(&self) -> SyncMode {
self.common_settings().sync_mode.unwrap_or(SyncMode::OneWay)
}
fn sync_deletions(&self) -> bool {
match self.sync_mode() {
SyncMode::TwoWay | SyncMode::OneWay => true,
SyncMode::OneWayNoDeletions => false,
}
}
fn sync_both(&self) -> bool {
match self.sync_mode() {
SyncMode::TwoWay => true,
SyncMode::OneWay | SyncMode::OneWayNoDeletions => false,
}
}
fn destination_folder(&self) -> Option<&str> {
self.common_settings().destination_folder.as_deref()
}
fn encryption_password(&self) -> Option<&str> {
self.common_settings().encryption_password.as_deref()
}
fn max_upload_rate(&self) -> Option<u64> {
self.common_settings().max_upload_rate
}
fn max_download_rate(&self) -> Option<u64> {
self.common_settings().max_download_rate
}
fn selective_sync(&self) -> Option<Vec<String>> {
self.common_settings().selective_sync.clone()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct OAuthCredentials {
pub client_id: String,
pub client_secret: String,
pub refresh_token: String,
#[serde(flatten)]
pub common: CommonProviderSettings,
}
impl ProviderConfig for OAuthCredentials {
fn common_settings(&self) -> &CommonProviderSettings {
&self.common
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct WebDAVCredentials {
pub url: String,
pub username: String,
pub password: String,
#[serde(flatten)]
pub common: CommonProviderSettings,
}
impl ProviderConfig for WebDAVCredentials {
fn common_settings(&self) -> &CommonProviderSettings {
&self.common
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct S3Credentials {
pub bucket: String,
pub region: String,
pub access_key_id: String,
pub secret_access_key: String,
pub endpoint: Option<String>,
#[serde(flatten)]
pub common: CommonProviderSettings,
}
impl ProviderConfig for S3Credentials {
fn common_settings(&self) -> &CommonProviderSettings {
&self.common
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct SFTPCredentials {
pub host: String,
pub port: Option<u16>,
pub username: String,
pub password: Option<String>,
pub private_key_path: Option<String>,
#[serde(flatten)]
pub common: CommonProviderSettings,
}
impl ProviderConfig for SFTPCredentials {
fn common_settings(&self) -> &CommonProviderSettings {
&self.common
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct NextcloudCredentials {
pub url: String,
pub username: String,
pub app_password: String,
#[serde(flatten)]
pub common: CommonProviderSettings,
}
impl ProviderConfig for NextcloudCredentials {
fn common_settings(&self) -> &CommonProviderSettings {
&self.common
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct MegaCredentials {
pub email: String,
pub password: String,
#[serde(flatten)]
pub common: CommonProviderSettings,
}
impl ProviderConfig for MegaCredentials {
fn common_settings(&self) -> &CommonProviderSettings {
&self.common
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct AzureBlobCredentials {
pub account_name: String,
pub account_key: String,
pub container: String,
pub endpoint: Option<String>,
#[serde(flatten)]
pub common: CommonProviderSettings,
}
impl ProviderConfig for AzureBlobCredentials {
fn common_settings(&self) -> &CommonProviderSettings {
&self.common
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct GCSCredentials {
pub bucket: String,
pub service_account_key_path: String,
pub endpoint: Option<String>,
#[serde(flatten)]
pub common: CommonProviderSettings,
}
impl ProviderConfig for GCSCredentials {
fn common_settings(&self) -> &CommonProviderSettings {
&self.common
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct B2Credentials {
pub bucket: String,
pub key_id: String,
pub application_key: String,
pub endpoint: Option<String>,
#[serde(flatten)]
pub common: CommonProviderSettings,
}
impl ProviderConfig for B2Credentials {
fn common_settings(&self) -> &CommonProviderSettings {
&self.common
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct PCloudCredentials {
pub access_token: String,
pub endpoint: Option<String>,
#[serde(flatten)]
pub common: CommonProviderSettings,
}
impl ProviderConfig for PCloudCredentials {
fn common_settings(&self) -> &CommonProviderSettings {
&self.common
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct IPFSCredentials {
pub jwt_token: String,
pub endpoint: Option<String>,
pub gateway_url: Option<String>,
#[serde(flatten)]
pub common: CommonProviderSettings,
}
impl ProviderConfig for IPFSCredentials {
fn common_settings(&self) -> &CommonProviderSettings {
&self.common
}
}
#[cfg(feature = "google_drive")]
pub mod google_drive;
#[cfg(feature = "dropbox")]
pub mod dropbox;
#[cfg(feature = "onedrive")]
pub mod onedrive;
#[cfg(feature = "webdav")]
pub mod webdav;
#[cfg(feature = "s3")]
pub mod s3;
#[cfg(feature = "sftp")]
pub mod sftp;
#[cfg(feature = "nextcloud")]
pub mod nextcloud;
#[cfg(feature = "box")]
pub mod box_provider;
#[cfg(feature = "mega")]
pub mod mega_provider;
#[cfg(feature = "azure_blob")]
pub mod azure_blob;
#[cfg(feature = "gcs")]
pub mod gcs;
#[cfg(feature = "b2")]
pub mod b2;
#[cfg(feature = "pcloud")]
pub mod pcloud;
#[cfg(feature = "ipfs")]
pub mod ipfs;
pub mod local_sim;
pub mod utils;
pub mod fallback;
pub mod encryption;
#[cfg(feature = "google_drive")]
pub use google_drive::{GoogleDriveProvider, GoogleDriveProviderBuilder};
#[cfg(feature = "dropbox")]
pub use dropbox::{DropboxProvider, DropboxProviderBuilder};
#[cfg(feature = "onedrive")]
pub use onedrive::{OneDriveProvider, OneDriveProviderBuilder};
#[cfg(feature = "webdav")]
pub use webdav::{WebDAVProvider, WebDAVProviderBuilder};
#[cfg(feature = "s3")]
pub use s3::{S3Provider, S3ProviderBuilder};
#[cfg(feature = "sftp")]
pub use sftp::{SFTPProvider, SFTPProviderBuilder};
#[cfg(feature = "nextcloud")]
pub use nextcloud::{NextcloudProvider, NextcloudProviderBuilder};
#[cfg(feature = "box")]
pub use box_provider::{BoxProvider, BoxProviderBuilder};
#[cfg(feature = "mega")]
pub use mega_provider::{MegaProvider, MegaProviderBuilder};
#[cfg(feature = "azure_blob")]
pub use azure_blob::{AzureBlobProvider, AzureBlobProviderBuilder};
#[cfg(feature = "gcs")]
pub use gcs::{GCSProvider, GCSProviderBuilder};
#[cfg(feature = "b2")]
pub use b2::{B2Provider, B2ProviderBuilder};
#[cfg(feature = "pcloud")]
pub use pcloud::{PCloudProvider, PCloudProviderBuilder};
#[cfg(feature = "ipfs")]
pub use ipfs::{IPFSProvider, IPFSProviderBuilder};
pub use fallback::SimulatedFallback;
pub use encryption::EncryptedBackend;
pub use utils::OAuthTokenManager;
use std::sync::Arc;
use crate::traits::StorageBackend;
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, zeroize::Zeroize)]
#[serde(tag = "type", content = "config")]
pub enum BackendCredentials {
#[cfg(feature = "google_drive")]
GoogleDrive(OAuthCredentials),
#[cfg(feature = "dropbox")]
Dropbox(OAuthCredentials),
#[cfg(feature = "onedrive")]
OneDrive(OAuthCredentials),
#[cfg(feature = "webdav")]
WebDAV(WebDAVCredentials),
#[cfg(feature = "s3")]
S3(S3Credentials),
#[cfg(feature = "sftp")]
SFTP(SFTPCredentials),
#[cfg(feature = "nextcloud")]
Nextcloud(NextcloudCredentials),
#[cfg(feature = "box")]
Box(OAuthCredentials),
#[cfg(feature = "mega")]
Mega(MegaCredentials),
#[cfg(feature = "azure_blob")]
AzureBlob(AzureBlobCredentials),
#[cfg(feature = "gcs")]
GCS(GCSCredentials),
#[cfg(feature = "b2")]
B2(B2Credentials),
#[cfg(feature = "pcloud")]
PCloud(PCloudCredentials),
#[cfg(feature = "ipfs")]
IPFS(IPFSCredentials),
}
#[allow(unreachable_patterns)]
impl ProviderConfig for BackendCredentials {
fn common_settings(&self) -> &CommonProviderSettings {
match self {
#[cfg(feature = "google_drive")]
BackendCredentials::GoogleDrive(c) => c.common_settings(),
#[cfg(feature = "dropbox")]
BackendCredentials::Dropbox(c) => c.common_settings(),
#[cfg(feature = "onedrive")]
BackendCredentials::OneDrive(c) => c.common_settings(),
#[cfg(feature = "webdav")]
BackendCredentials::WebDAV(c) => c.common_settings(),
#[cfg(feature = "s3")]
BackendCredentials::S3(c) => c.common_settings(),
#[cfg(feature = "sftp")]
BackendCredentials::SFTP(c) => c.common_settings(),
#[cfg(feature = "nextcloud")]
BackendCredentials::Nextcloud(c) => c.common_settings(),
#[cfg(feature = "box")]
BackendCredentials::Box(c) => c.common_settings(),
#[cfg(feature = "mega")]
BackendCredentials::Mega(c) => c.common_settings(),
#[cfg(feature = "azure_blob")]
BackendCredentials::AzureBlob(c) => c.common_settings(),
#[cfg(feature = "gcs")]
BackendCredentials::GCS(c) => c.common_settings(),
#[cfg(feature = "b2")]
BackendCredentials::B2(c) => c.common_settings(),
#[cfg(feature = "pcloud")]
BackendCredentials::PCloud(c) => c.common_settings(),
#[cfg(feature = "ipfs")]
BackendCredentials::IPFS(c) => c.common_settings(),
_ => unreachable!(),
}
}
}
impl BackendCredentials {
pub fn sync_mode(&self) -> SyncMode {
ProviderConfig::sync_mode(self)
}
pub fn selective_sync(&self) -> Option<Vec<String>> {
ProviderConfig::selective_sync(self)
}
}
pub struct BackendRegistry;
impl BackendRegistry {
pub fn build(mut creds: BackendCredentials) -> Arc<dyn StorageBackend> {
match &mut creds {
#[cfg(feature = "google_drive")]
BackendCredentials::GoogleDrive(ref mut c) => {
c.client_secret = utils::get_secure_credential("google_drive", "client_secret", &c.client_secret);
c.refresh_token = utils::get_secure_credential("google_drive", "refresh_token", &c.refresh_token);
}
#[cfg(feature = "dropbox")]
BackendCredentials::Dropbox(ref mut c) => {
c.client_secret = utils::get_secure_credential("dropbox", "client_secret", &c.client_secret);
c.refresh_token = utils::get_secure_credential("dropbox", "refresh_token", &c.refresh_token);
}
#[cfg(feature = "onedrive")]
BackendCredentials::OneDrive(ref mut c) => {
c.client_secret = utils::get_secure_credential("onedrive", "client_secret", &c.client_secret);
c.refresh_token = utils::get_secure_credential("onedrive", "refresh_token", &c.refresh_token);
}
#[cfg(feature = "webdav")]
BackendCredentials::WebDAV(ref mut c) => {
c.password = utils::get_secure_credential("webdav", "password", &c.password);
}
#[cfg(feature = "s3")]
BackendCredentials::S3(ref mut c) => {
c.secret_access_key = utils::get_secure_credential("s3", "secret_access_key", &c.secret_access_key);
}
#[cfg(feature = "sftp")]
BackendCredentials::SFTP(ref mut c) => {
if let Some(ref mut pwd) = c.password {
*pwd = utils::get_secure_credential("sftp", "password", pwd);
}
}
#[cfg(feature = "nextcloud")]
BackendCredentials::Nextcloud(ref mut c) => {
c.app_password = utils::get_secure_credential("nextcloud", "app_password", &c.app_password);
}
#[cfg(feature = "box")]
BackendCredentials::Box(ref mut c) => {
c.client_secret = utils::get_secure_credential("box", "client_secret", &c.client_secret);
c.refresh_token = utils::get_secure_credential("box", "refresh_token", &c.refresh_token);
}
#[cfg(feature = "mega")]
BackendCredentials::Mega(ref mut c) => {
c.password = utils::get_secure_credential("mega", "password", &c.password);
}
#[cfg(feature = "azure_blob")]
BackendCredentials::AzureBlob(ref mut c) => {
c.account_key = utils::get_secure_credential("azure_blob", "account_key", &c.account_key);
}
#[cfg(feature = "b2")]
BackendCredentials::B2(ref mut c) => {
c.application_key = utils::get_secure_credential("b2", "application_key", &c.application_key);
}
#[cfg(feature = "pcloud")]
BackendCredentials::PCloud(ref mut c) => {
c.access_token = utils::get_secure_credential("pcloud", "access_token", &c.access_token);
}
#[cfg(feature = "ipfs")]
BackendCredentials::IPFS(ref mut c) => {
c.jwt_token = utils::get_secure_credential("ipfs", "jwt_token", &c.jwt_token);
}
_ => {}
}
match creds {
#[cfg(feature = "google_drive")]
BackendCredentials::GoogleDrive(c) => Arc::new(GoogleDriveProvider::new(c)),
#[cfg(feature = "dropbox")]
BackendCredentials::Dropbox(c) => Arc::new(DropboxProvider::new(c)),
#[cfg(feature = "onedrive")]
BackendCredentials::OneDrive(c) => Arc::new(OneDriveProvider::new(c)),
#[cfg(feature = "webdav")]
BackendCredentials::WebDAV(c) => Arc::new(WebDAVProvider::new(c)),
#[cfg(feature = "s3")]
BackendCredentials::S3(c) => Arc::new(S3Provider::new(c)),
#[cfg(feature = "sftp")]
BackendCredentials::SFTP(c) => Arc::new(SFTPProvider::new(c)),
#[cfg(feature = "nextcloud")]
BackendCredentials::Nextcloud(c) => Arc::new(NextcloudProvider::new(c)),
#[cfg(feature = "box")]
BackendCredentials::Box(c) => Arc::new(BoxProvider::new(c)),
#[cfg(feature = "mega")]
BackendCredentials::Mega(c) => Arc::new(MegaProvider::new(c)),
#[cfg(feature = "azure_blob")]
BackendCredentials::AzureBlob(c) => Arc::new(AzureBlobProvider::new(c)),
#[cfg(feature = "gcs")]
BackendCredentials::GCS(c) => Arc::new(GCSProvider::new(c)),
#[cfg(feature = "b2")]
BackendCredentials::B2(c) => Arc::new(B2Provider::new(c)),
#[cfg(feature = "pcloud")]
BackendCredentials::PCloud(c) => Arc::new(PCloudProvider::new(c)),
#[cfg(feature = "ipfs")]
BackendCredentials::IPFS(c) => Arc::new(IPFSProvider::new(c)),
}
}
#[allow(unreachable_patterns, unreachable_code, unused_variables)]
pub fn build_wrapped(
creds: BackendCredentials,
sim_root: std::path::PathBuf,
global_upload_limiter: Option<crate::rate_limit::TokenBucket>,
global_download_limiter: Option<crate::rate_limit::TokenBucket>,
) -> Arc<dyn StorageBackend> {
let provider_name: &str = match &creds {
#[cfg(feature = "google_drive")]
BackendCredentials::GoogleDrive(_) => "Google Drive",
#[cfg(feature = "dropbox")]
BackendCredentials::Dropbox(_) => "Dropbox",
#[cfg(feature = "onedrive")]
BackendCredentials::OneDrive(_) => "OneDrive",
#[cfg(feature = "webdav")]
BackendCredentials::WebDAV(_) => "WebDAV",
#[cfg(feature = "s3")]
BackendCredentials::S3(_) => "S3",
#[cfg(feature = "sftp")]
BackendCredentials::SFTP(_) => "SFTP",
#[cfg(feature = "nextcloud")]
BackendCredentials::Nextcloud(_) => "Nextcloud",
#[cfg(feature = "box")]
BackendCredentials::Box(_) => "Box",
#[cfg(feature = "mega")]
BackendCredentials::Mega(_) => "MEGA",
#[cfg(feature = "azure_blob")]
BackendCredentials::AzureBlob(_) => "Azure Blob",
#[cfg(feature = "gcs")]
BackendCredentials::GCS(_) => "GCS",
#[cfg(feature = "b2")]
BackendCredentials::B2(_) => "B2",
#[cfg(feature = "pcloud")]
BackendCredentials::PCloud(_) => "pCloud",
#[cfg(feature = "ipfs")]
BackendCredentials::IPFS(_) => "IPFS",
_ => unreachable!(),
};
let sync_mode = creds.sync_mode();
let max_upload_rate = creds.max_upload_rate();
let max_download_rate = creds.max_download_rate();
let encryption_password = creds.encryption_password();
let upload_limiter = max_upload_rate
.map(|rate| crate::rate_limit::TokenBucket::new(rate * 1024))
.or(global_upload_limiter);
let download_limiter = max_download_rate
.map(|rate| crate::rate_limit::TokenBucket::new(rate * 1024))
.or(global_download_limiter);
let inner = Self::build(creds.clone());
let local_sim = local_sim::LocalSimulation::new(sim_root, provider_name.to_string())
.with_limiters(upload_limiter.clone(), download_limiter.clone());
let fallback = fallback::SimulatedFallback::new(Some(inner), local_sim, provider_name, sync_mode);
let rate_limited = crate::rate_limit::RateLimitingBackend::new(
fallback,
upload_limiter,
download_limiter,
);
if let Some(password) = encryption_password {
Arc::new(encryption::EncryptedBackend::new(rate_limited, password))
} else {
Arc::new(rate_limited)
}
}
pub fn create_backend(
creds: BackendCredentials,
sim_root: std::path::PathBuf,
) -> Arc<dyn StorageBackend> {
Self::build_wrapped(creds, sim_root, None, None)
}
}