1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use super::types::Server;
use crate::errors::Result;
#[cfg(feature = "server-aws")]
pub use crate::server::cloud::aws::AwsCredentials;
#[cfg(feature = "server-aws")]
use crate::server::cloud::aws::AwsService;
#[cfg(feature = "server-gcp")]
use crate::server::cloud::gcp::GcpService;
#[cfg(feature = "cloud")]
use crate::server::cloud::CloudServer;
#[cfg(feature = "server-local")]
use crate::server::local::LocalServer;
#[cfg(feature = "server-sync")]
use crate::server::sync::SyncServer;
#[cfg(feature = "server-local")]
use std::path::PathBuf;
#[cfg(feature = "server-sync")]
use uuid::Uuid;
/// The configuration for a replica's access to a sync server.
///
/// This enum is non-exhaustive, as users should only be constructing required
/// variants, not matching on it.
#[non_exhaustive]
pub enum ServerConfig {
/// A local task database, for situations with a single replica.
#[cfg(feature = "server-local")]
Local {
/// Path containing the server's DB
server_dir: PathBuf,
},
/// A remote taskchampion-sync-server instance
#[cfg(feature = "server-sync")]
Remote {
/// The base URL of the Sync server
url: String,
/// Client ID to identify and authenticate this replica to the server
client_id: Uuid,
/// Private encryption secret used to encrypt all data sent to the server. This can
/// be any suitably un-guessable string of bytes.
encryption_secret: Vec<u8>,
},
/// A Google Cloud Platform storage bucket.
#[cfg(feature = "server-gcp")]
Gcp {
/// Bucket in which to store the task data. This bucket must not be used for any other
/// purpose.
///
/// No special bucket configuration is reqiured.
bucket: String,
/// Path to a GCP credential file, in JSON format.
///
/// If `None`, then [Application Default
/// Credentials](https://cloud.google.com/docs/authentication/application-default-credentials)
/// are used. Typically these are associated with the user's Google Cloud account.
///
/// If `Some(path)`, then the path must be to a service account key. The service account
/// must have a role with the following permissions:
///
/// - storage.buckets.create
/// - storage.buckets.get
/// - storage.buckets.update
/// - storage.objects.create
/// - storage.objects.get
/// - storage.objects.list
/// - storage.objects.update
/// - storage.objects.delete
///
/// See the following GCP resources for more information:
/// - <https://cloud.google.com/docs/authentication#service-accounts>
/// - <https://cloud.google.com/iam/docs/keys-create-delete#creating>
credential_path: Option<String>,
/// Private encryption secret used to encrypt all data sent to the server. This can
/// be any suitably un-guessable string of bytes.
encryption_secret: Vec<u8>,
},
/// An Amazon Web Services storage bucket.
///
/// This configuration supports S3-compatibile services, by specifying an endpoint URL, forcing
/// path style URLs, and omitting the region. In particular, support has been confirmed for
/// minio. Contributions are welcome to document tested support for additional S3-compatible
/// services.
#[cfg(feature = "server-aws")]
Aws {
/// Region in which the bucket is located.
/// If `None`, the default region is used.
/// The `default` region is based on the AWS SDK
/// - <https://docs.aws.amazon.com/sdk-for-rust/latest/dg/region.html>
///
/// following, in order:
/// 1. `AWS_REGION` environment variable,
/// 2. `AWS_CONFIG_FILE` environment variable and the `region` in that file
/// 3. `AWS_PROFILE` variable and the region for that file in the config file
///
/// Failing all of those, we will default to `us-east-1`.
///
/// Note that instance metadata (IMDS) is not included here.
region: Option<String>,
/// Bucket in which to store the task data.
///
/// This bucket must not be used for any other purpose. No special bucket configuration is
/// required.
bucket: String,
/// An optional URL to specify the hostname of an s3-compatible service. When endpoint_url
/// is used, region is ignored by the underlying S3 client.
endpoint_url: Option<String>,
/// If set, force the S3 client to use path-style URLs instead of virtual-hosted-style
/// (subdomain) URLs for the bucket.
force_path_style: bool,
/// Credential configuration for access to the bucket.
credentials: AwsCredentials,
/// Private encryption secret used to encrypt all data sent to the server. This can
/// be any suitably un-guessable string of bytes.
encryption_secret: Vec<u8>,
},
}
impl ServerConfig {
/// Get a server based on this configuration
pub async fn into_server(self) -> Result<Box<dyn Server>> {
// This expression is unreachable if no server features are enabled.
#[allow(unreachable_code)]
Ok(match self {
#[cfg(feature = "server-local")]
ServerConfig::Local { server_dir } => Box::new(LocalServer::new(server_dir)?),
#[cfg(feature = "server-sync")]
ServerConfig::Remote {
url,
client_id,
encryption_secret,
} => Box::new(SyncServer::new(url, client_id, encryption_secret)?),
#[cfg(feature = "server-gcp")]
ServerConfig::Gcp {
bucket,
credential_path,
encryption_secret,
} => Box::new(
CloudServer::new(
GcpService::new(bucket, credential_path).await?,
encryption_secret,
)
.await?,
),
#[cfg(feature = "server-aws")]
ServerConfig::Aws {
region,
bucket,
credentials,
encryption_secret,
endpoint_url,
force_path_style,
} => Box::new(
CloudServer::new(
AwsService::new(region, bucket, credentials, endpoint_url, force_path_style)
.await?,
encryption_secret,
)
.await?,
),
})
}
}