Skip to content

Commit c758c5d

Browse files
metamejoepio
authored andcommitted
Store config in FileStore
1 parent e59ea32 commit c758c5d

File tree

4 files changed

+72
-79
lines changed

4 files changed

+72
-79
lines changed

server/src/config.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Parse CLI options, setup on boot, read .env values
22
33
use crate::errors::AtomicServerResult;
4+
use crate::files::FileStore;
45
use clap::Parser;
56
use dotenv::dotenv;
67
use std::net::IpAddr;
@@ -206,6 +207,10 @@ pub struct Config {
206207
pub uploads_path: PathBuf,
207208
/// Path to where the search index for tantivy full text search is located
208209
pub search_index_path: PathBuf,
210+
/// stores config values and the active FileStore type, e.g. FS or S3
211+
pub file_store: FileStore,
212+
/// stores config values for FS filestore regardless of active file store as fallback
213+
pub fs_file_store: FileStore,
209214
/// If true, the initialization scripts will be ran (create first Drive, Agent, indexing, etc)
210215
pub initialize: bool,
211216
}
@@ -265,6 +270,10 @@ pub fn build_config(opts: Opts) -> AtomicServerResult<Config> {
265270

266271
let initialize = !std::path::Path::exists(&store_path) || opts.initialize;
267272

273+
// Initialize FileStore config
274+
let fs_file_store = FileStore::init_fs_from_config(&opts);
275+
let file_store = FileStore::init_from_config(&opts, fs_file_store.clone());
276+
268277
if opts.https & opts.email.is_none() {
269278
return Err(
270279
"The `--email` flag (or ATOMIC_EMAIL env) is required for getting an HTTPS certificate from letsencrypt.org."
@@ -296,5 +305,7 @@ pub fn build_config(opts: Opts) -> AtomicServerResult<Config> {
296305
store_path,
297306
search_index_path,
298307
uploads_path,
308+
file_store,
309+
fs_file_store,
299310
})
300311
}

server/src/files.rs

Lines changed: 55 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,59 @@ use std::{fmt, path::PathBuf, time::Duration};
33
use opendal::{services::S3, Operator};
44
use tokio::fs::File;
55

6-
use crate::{appstate::AppState, config::Config, errors::AtomicServerResult};
6+
use crate::{config::{Config, Opts}, errors::AtomicServerResult};
77

8+
#[derive(Clone, Debug)]
89
pub enum FileStore {
9-
S3,
10-
FS,
10+
S3(S3Config),
11+
FS(FSConfig),
1112
}
1213

14+
#[derive(Clone, Debug)]
15+
pub struct S3Config {
16+
bucket: String,
17+
path: String,
18+
endpoint: Option<String>,
19+
region: Option<String>,
20+
}
21+
22+
#[derive(Clone, Debug)]
23+
pub struct FSConfig {}
24+
1325
impl FileStore {
1426
const S3_PREFIX: &'static str = "s3:";
1527
const FS_PREFIX: &'static str = "fs:";
1628

17-
pub fn get_config_file_store(config: &Config) -> FileStore {
18-
if config.opts.s3_bucket.is_some() {
19-
FileStore::S3
29+
pub fn init_fs_from_config(_opts: &Opts) -> FileStore {
30+
FileStore::FS(FSConfig {})
31+
}
32+
33+
pub fn init_from_config(opts: &Opts, fs_file_store: FileStore) -> FileStore {
34+
if opts.s3_bucket.is_some() {
35+
let config = S3Config {
36+
bucket: opts.s3_bucket.clone().unwrap(),
37+
endpoint: opts.s3_endpoint.clone(),
38+
region: opts.s3_region.clone(),
39+
path: opts.s3_path.clone().unwrap_or("uploads".to_string()),
40+
};
41+
FileStore::S3(config)
2042
} else {
21-
FileStore::FS
43+
fs_file_store
2244
}
2345
}
2446

25-
pub fn get_subject_file_store(subject: &str) -> FileStore {
47+
pub fn get_subject_file_store<'a>(config: &'a Config, subject: &str) -> &'a FileStore {
2648
if subject.contains(Self::S3_PREFIX) {
27-
FileStore::S3
49+
&config.file_store
2850
} else {
29-
FileStore::FS
51+
&config.fs_file_store
3052
}
3153
}
3254

3355
pub fn prefix(&self) -> &str {
3456
match self {
35-
Self::S3 => Self::S3_PREFIX,
36-
Self::FS => Self::FS_PREFIX,
57+
Self::S3(_) => Self::S3_PREFIX,
58+
Self::FS(_) => Self::FS_PREFIX,
3759
}
3860
}
3961

@@ -49,92 +71,52 @@ impl fmt::Display for FileStore {
4971
}
5072

5173
pub async fn s3_upload_object(
52-
appstate: &AppState,
74+
file_store: &FileStore,
5375
file_id: &str,
5476
file_path: &PathBuf,
5577
) -> AtomicServerResult<()> {
5678
let mut builder = S3::default();
57-
let bucket = appstate
58-
.config
59-
.opts
60-
.s3_bucket
61-
.as_ref()
62-
.ok_or("uploading to s3 but no s3 bucket configured")?;
63-
builder.bucket(bucket);
64-
appstate
65-
.config
66-
.opts
67-
.s3_region
68-
.as_ref()
69-
.map(|r| builder.region(&r));
70-
appstate
71-
.config
72-
.opts
73-
.s3_endpoint
74-
.as_ref()
75-
.map(|e| builder.endpoint(&e));
76-
77-
let default_path = &"uploads".to_string();
78-
let path = appstate
79-
.config
80-
.opts
81-
.s3_path
82-
.as_ref()
83-
.unwrap_or(default_path);
79+
80+
if let FileStore::S3(config) = file_store {
81+
builder.bucket(&config.bucket);
82+
builder.root(&config.path);
83+
config.region.as_ref().map(|r| builder.region(&r));
84+
config.endpoint.as_ref().map(|e| builder.endpoint(&e));
85+
} else {
86+
return Err("Uploading to S3 but no S3 config provided".into());
87+
}
8488

8589
let op: Operator = Operator::new(builder)?.finish();
8690

8791
let mut tmp_file = File::open(file_path).await?;
8892
let length = tmp_file.metadata().await?.len();
8993

90-
let s3_path = format!("{}/{}", path, &file_id);
91-
let mut w = op.writer_with(&s3_path).content_length(length).await?;
94+
let mut w = op.writer_with(&file_id).content_length(length).await?;
9295
tokio::io::copy(&mut tmp_file, &mut w).await?;
9396
w.close().await?;
9497
Ok(())
9598
}
9699

97100
pub async fn get_s3_signed_url(
98-
appstate: &AppState,
101+
file_store: &FileStore,
99102
duration: Duration,
100103
file_id: &str,
101104
) -> AtomicServerResult<String> {
102105
let mut builder = S3::default();
103106

104-
let bucket = appstate
105-
.config
106-
.opts
107-
.s3_bucket
108-
.as_ref()
109-
.ok_or("s3 file found but no s3 bucket")?;
110-
builder.bucket(bucket);
111-
112-
appstate
113-
.config
114-
.opts
115-
.s3_region
116-
.as_ref()
117-
.map(|r| builder.region(&r));
118-
119-
appstate
120-
.config
121-
.opts
122-
.s3_endpoint
123-
.as_ref()
124-
.map(|e| builder.endpoint(&e));
125-
126-
let default_path = &"uploads".to_string();
127-
let path = appstate
128-
.config
129-
.opts
130-
.s3_path
131-
.as_ref()
132-
.unwrap_or(default_path);
107+
if let FileStore::S3(config) = file_store {
108+
builder.bucket(&config.bucket);
109+
builder.root(&config.path);
110+
config.region.as_ref().map(|r| builder.region(&r));
111+
config.endpoint.as_ref().map(|e| builder.endpoint(&e));
112+
} else {
113+
return Err("Downloading from S3 but no S3 config provided".into());
114+
}
133115

134116
let op: Operator = Operator::new(builder)?.finish();
135117

136118
let uri = op
137-
.presign_read(&format!("{}/{}", &path, file_id), duration)
119+
.presign_read(file_id, duration)
138120
.await?
139121
.uri()
140122
.to_string();

server/src/handlers/download.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ pub async fn handle_download(
3333

3434
let for_agent = get_client_agent(headers, &appstate, subject.clone())?;
3535
tracing::info!("handle_download: {}", subject);
36-
let file_store = FileStore::get_subject_file_store(&subject);
36+
let file_store = FileStore::get_subject_file_store(&appstate.config, &subject);
3737
let encoded = subject.replace(file_store.prefix(), &file_store.encoded());
3838
let resource = store.get_resource_extended(&encoded, false, &for_agent)?;
3939
let file_id = resource
4040
.get(urls::INTERNAL_ID)
4141
.map_err(|e| format!("Internal ID of file could not be resolved. {}", e))?
4242
.to_string();
4343

44-
if let FileStore::S3 = file_store {
44+
if let FileStore::S3(_) = file_store {
4545
signed_url_redirect_handler(file_id.as_str(), &req, &appstate).await
4646
} else {
4747
download_file_handler_partial(file_id.as_str(), &req, &appstate)
@@ -65,7 +65,7 @@ async fn signed_url_redirect_handler(
6565
req: &HttpRequest,
6666
appstate: &AppState,
6767
) -> AtomicServerResult<HttpResponse> {
68-
let signed_url = files::get_s3_signed_url(appstate, Duration::from_secs(3600), file_id).await?;
68+
let signed_url = files::get_s3_signed_url(&appstate.config.file_store, Duration::from_secs(3600), file_id).await?;
6969
Ok(web::Redirect::to(signed_url)
7070
.respond_to(req)
7171
.map_into_boxed_body())

server/src/handlers/upload.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ pub async fn upload_handler(
8585
.try_into()
8686
.map_err(|_e| "Too large")?;
8787

88-
let file_store = FileStore::get_config_file_store(&appstate.config);
88+
let file_store = &appstate.config.file_store;
8989
let file_id = format!("{}{}", file_store.prefix(), &fs_file_id);
90-
if let FileStore::S3 = file_store {
91-
files::s3_upload_object(&appstate, &file_id, &file_path).await?;
90+
if let FileStore::S3(_) = file_store {
91+
files::s3_upload_object(&file_store, &file_id, &file_path).await?;
9292
fs::remove_file(&file_path)?;
9393
}
9494

0 commit comments

Comments
 (0)