Skip to content

Commit 3c52750

Browse files
metamejoepio
authored andcommitted
refactor files logic into files module
1 parent d19a088 commit 3c52750

File tree

5 files changed

+146
-88
lines changed

5 files changed

+146
-88
lines changed

server/src/bin.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod commit_monitor;
88
pub mod config;
99
mod content_types;
1010
mod errors;
11+
mod files;
1112
mod handlers;
1213
mod helpers;
1314
#[cfg(feature = "https")]

server/src/files.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
use std::{fmt, path::PathBuf, time::Duration};
2+
3+
use opendal::{services::S3, Operator};
4+
5+
use crate::{appstate::AppState, config::Config, errors::AtomicServerResult};
6+
7+
pub enum FileStore {
8+
S3,
9+
FS,
10+
}
11+
12+
impl FileStore {
13+
pub fn get_file_store(file_id: &str) -> FileStore {
14+
if file_id.starts_with("s3:") {
15+
FileStore::S3
16+
} else {
17+
FileStore::FS
18+
}
19+
}
20+
21+
pub fn get_config_file_store(config: &Config) -> FileStore {
22+
if config.opts.s3_bucket.is_some() {
23+
FileStore::S3
24+
} else {
25+
FileStore::FS
26+
}
27+
}
28+
29+
pub fn get_subject_file_store(subject: &str) -> FileStore {
30+
if subject.contains("s3:") {
31+
FileStore::S3
32+
} else {
33+
FileStore::FS
34+
}
35+
}
36+
37+
pub fn name(&self) -> &str {
38+
match self {
39+
Self::S3 => "s3",
40+
Self::FS => "fs",
41+
}
42+
}
43+
44+
}
45+
46+
impl fmt::Display for FileStore {
47+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48+
write!(f, "{}", self.name())
49+
}
50+
}
51+
52+
pub async fn s3_upload_object(
53+
appstate: &AppState,
54+
file_id: &str,
55+
file_path: &PathBuf,
56+
) -> AtomicServerResult<()> {
57+
let mut builder = S3::default();
58+
builder.bucket(appstate.config.opts.s3_bucket.as_ref().unwrap().as_str());
59+
appstate
60+
.config
61+
.opts
62+
.s3_region
63+
.as_ref()
64+
.map(|r| builder.region(&r));
65+
appstate
66+
.config
67+
.opts
68+
.s3_endpoint
69+
.as_ref()
70+
.map(|e| builder.endpoint(&e));
71+
72+
let default_path = &"uploads".to_string();
73+
let path = appstate
74+
.config
75+
.opts
76+
.s3_path
77+
.as_ref()
78+
.unwrap_or(default_path);
79+
80+
let op: Operator = Operator::new(builder)?.finish();
81+
82+
let buffer = std::fs::read(file_path)?;
83+
op.write(&format!("{}/{}", path, &file_id), buffer).await?;
84+
85+
Ok(())
86+
}
87+
88+
pub async fn get_s3_signed_url(
89+
appstate: &AppState,
90+
duration: Duration,
91+
file_id: &str,
92+
) -> AtomicServerResult<String> {
93+
let mut builder = S3::default();
94+
95+
let bucket = appstate
96+
.config
97+
.opts
98+
.s3_bucket
99+
.as_ref()
100+
.ok_or("s3 file found but no s3 bucket")?;
101+
builder.bucket(bucket);
102+
103+
appstate
104+
.config
105+
.opts
106+
.s3_region
107+
.as_ref()
108+
.map(|r| builder.region(&r));
109+
110+
appstate
111+
.config
112+
.opts
113+
.s3_endpoint
114+
.as_ref()
115+
.map(|e| builder.endpoint(&e));
116+
117+
let default_path = &"uploads".to_string();
118+
let path = appstate
119+
.config
120+
.opts
121+
.s3_path
122+
.as_ref()
123+
.unwrap_or(default_path);
124+
125+
let op: Operator = Operator::new(builder)?.finish();
126+
127+
let uri = op.presign_read(&format!("{}/{}", &path, file_id), duration).await?.uri().to_string();
128+
129+
Ok(uri)
130+
}

server/src/handlers/download.rs

Lines changed: 9 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ use std::time::Duration;
33
use actix_files::NamedFile;
44
use actix_web::{web, HttpRequest, HttpResponse, Responder};
55
use atomic_lib::{urls, Storelike};
6-
use opendal::{services::S3, Operator};
76

8-
use crate::{appstate::AppState, errors::AtomicServerResult, helpers::get_client_agent};
7+
use crate::{appstate::AppState, errors::AtomicServerResult, files::{self, FileStore}, helpers::get_client_agent};
98

109
/// Downloads the File of the Resource that matches the same URL minus the `/download` path.
1110
#[tracing::instrument(skip(appstate, req))]
@@ -29,13 +28,15 @@ pub async fn handle_download(
2928

3029
let for_agent = get_client_agent(headers, &appstate, subject.clone())?;
3130
tracing::info!("handle_download: {}", subject);
32-
let resource = store.get_resource_extended(&urlencoding::encode(&subject).into_owned(), false, &for_agent)?;
31+
let file_store = FileStore::get_subject_file_store(&subject);
32+
let encoded = subject.replace(&format!("{file_store}:"), &format!("{file_store}%3A"));
33+
let resource = store.get_resource_extended(&encoded, false, &for_agent)?;
3334
let file_id = resource
3435
.get(urls::INTERNAL_ID)
3536
.map_err(|e| format!("Internal ID of file could not be resolved. {}", e))?
3637
.to_string();
37-
let is_s3 = file_id.starts_with("s3:");
38-
if is_s3 {
38+
39+
if let FileStore::S3 = file_store {
3940
signed_url_redirect_handler(file_id.as_str(), &req, &appstate).await
4041
} else {
4142
download_file_handler_partial(file_id.as_str(), &req, &appstate)
@@ -47,7 +48,7 @@ pub fn download_file_handler_partial(
4748
req: &HttpRequest,
4849
appstate: &AppState,
4950
) -> AtomicServerResult<HttpResponse> {
50-
println!("{file_id:?}");
51+
tracing::info!("downloading from fs: {file_id:?}");
5152
let fs_file_id = file_id.strip_prefix("fs:").unwrap_or(file_id);
5253
let mut file_path = appstate.config.uploads_path.clone();
5354
file_path.push(fs_file_id.to_string());
@@ -60,45 +61,8 @@ async fn signed_url_redirect_handler(
6061
req: &HttpRequest,
6162
appstate: &AppState,
6263
) -> AtomicServerResult<HttpResponse> {
63-
let mut builder = S3::default();
64-
65-
let bucket = appstate
66-
.config
67-
.opts
68-
.s3_bucket
69-
.as_ref()
70-
.ok_or("s3 file found but no s3 bucket")?;
71-
builder.bucket(bucket);
72-
73-
appstate
74-
.config
75-
.opts
76-
.s3_region
77-
.as_ref()
78-
.map(|r| builder.region(&r));
79-
80-
appstate
81-
.config
82-
.opts
83-
.s3_endpoint
84-
.as_ref()
85-
.map(|e| builder.endpoint(&e));
86-
87-
let default_path = &"uploads".to_string();
88-
let path = appstate
89-
.config
90-
.opts
91-
.s3_path
92-
.as_ref()
93-
.unwrap_or(default_path);
94-
95-
let op: Operator = Operator::new(builder)?.finish();
96-
97-
let signed_url = op
98-
.presign_read(&format!("{}/{}", &path, file_id), Duration::from_secs(3600))
99-
.await?
100-
.uri()
101-
.to_string();
64+
let signed_url = files::get_s3_signed_url(appstate, Duration::from_secs(3600), file_id)
65+
.await?;
10266
Ok(web::Redirect::to(signed_url)
10367
.respond_to(req)
10468
.map_into_boxed_body())

server/src/handlers/upload.rs

Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::{
22
ffi::OsStr,
33
fs::{self, File},
44
io::Write,
5-
path::{Path, PathBuf},
5+
path::Path,
66
};
77

88
use actix_multipart::Multipart;
@@ -11,10 +11,9 @@ use atomic_lib::{
1111
commit::CommitResponse, hierarchy::check_write, urls, utils::now, Resource, Storelike, Value,
1212
};
1313
use futures::{StreamExt, TryStreamExt};
14-
use opendal::{services::S3, Operator};
1514
use serde::Deserialize;
1615

17-
use crate::{appstate::AppState, errors::AtomicServerResult, helpers::get_client_agent};
16+
use crate::{appstate::AppState, errors::AtomicServerResult, files::{self, FileStore}, helpers::get_client_agent};
1817

1918
#[derive(Deserialize, Debug)]
2019
pub struct UploadQuery {
@@ -81,11 +80,10 @@ pub async fn upload_handler(
8180
.try_into()
8281
.map_err(|_e| "Too large")?;
8382

84-
let is_s3 = appstate.config.opts.s3_bucket.is_some();
85-
let file_store = if is_s3 { "s3" } else { "fs" };
83+
let file_store = FileStore::get_config_file_store(&appstate.config);
8684
let file_id = format!("{}:{}", &file_store, &fs_file_id);
87-
if is_s3 {
88-
upload_object(&appstate, &file_id, &file_path).await?;
85+
if let FileStore::S3 = file_store {
86+
files::s3_upload_object(&appstate, &file_id, &file_path).await?;
8987
fs::remove_file(&file_path)?;
9088
}
9189

@@ -140,39 +138,3 @@ fn guess_mime_for_filename(filename: &str) -> String {
140138
fn get_extension_from_filename(filename: &str) -> Option<&str> {
141139
Path::new(filename).extension().and_then(OsStr::to_str)
142140
}
143-
144-
async fn upload_object(
145-
appstate: &AppState,
146-
file_id: &str,
147-
file_path: &PathBuf,
148-
) -> Result<(), std::io::Error> {
149-
let mut builder = S3::default();
150-
builder.bucket(appstate.config.opts.s3_bucket.as_ref().unwrap().as_str());
151-
appstate
152-
.config
153-
.opts
154-
.s3_region
155-
.as_ref()
156-
.map(|r| builder.region(&r));
157-
appstate
158-
.config
159-
.opts
160-
.s3_endpoint
161-
.as_ref()
162-
.map(|e| builder.endpoint(&e));
163-
164-
let default_path = &"uploads".to_string();
165-
let path = appstate
166-
.config
167-
.opts
168-
.s3_path
169-
.as_ref()
170-
.unwrap_or(default_path);
171-
172-
let op: Operator = Operator::new(builder)?.finish();
173-
174-
let buffer = std::fs::read(file_path)?;
175-
op.write(&format!("{}/{}", path, &file_id), buffer).await?;
176-
177-
Ok(())
178-
}

server/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod commit_monitor;
99
pub mod config;
1010
mod content_types;
1111
mod errors;
12+
mod files;
1213
mod handlers;
1314
mod helpers;
1415
#[cfg(feature = "https")]

0 commit comments

Comments
 (0)