cleanup code
This commit is contained in:
+85
-19
@@ -2,12 +2,14 @@ use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use aws_config::Region;
|
||||
use aws_credential_types::Credentials;
|
||||
use aws_sdk_s3::{config::Builder as S3ConfigBuilder, primitives::ByteStream, Client};
|
||||
use aws_sdk_s3::{Client, config::Builder as S3ConfigBuilder, primitives::ByteStream};
|
||||
use bytes::Bytes;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum StorageConfig {
|
||||
Local { root: PathBuf },
|
||||
Local {
|
||||
root: PathBuf,
|
||||
},
|
||||
S3 {
|
||||
endpoint: Option<String>,
|
||||
region: String,
|
||||
@@ -40,8 +42,16 @@ impl Storage {
|
||||
tokio::fs::create_dir_all(&root).await?;
|
||||
Ok(Self::Local { root })
|
||||
}
|
||||
StorageConfig::S3 { endpoint, region, bucket, access_key, secret_key, force_path_style } => {
|
||||
let credentials = Credentials::new(access_key, secret_key, None, None, "rustpad-env");
|
||||
StorageConfig::S3 {
|
||||
endpoint,
|
||||
region,
|
||||
bucket,
|
||||
access_key,
|
||||
secret_key,
|
||||
force_path_style,
|
||||
} => {
|
||||
let credentials =
|
||||
Credentials::new(access_key, secret_key, None, None, "rustpad-env");
|
||||
let shared = aws_config::defaults(aws_config::BehaviorVersion::latest())
|
||||
.region(Region::new(region.clone()))
|
||||
.credentials_provider(credentials)
|
||||
@@ -53,42 +63,70 @@ impl Storage {
|
||||
if let Some(endpoint) = endpoint.filter(|value| !value.trim().is_empty()) {
|
||||
builder = builder.endpoint_url(endpoint);
|
||||
}
|
||||
Ok(Self::S3 { client: Client::from_conf(builder.build()), bucket: Arc::from(bucket) })
|
||||
Ok(Self::S3 {
|
||||
client: Client::from_conf(builder.build()),
|
||||
bucket: Arc::from(bucket),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn backend_name(&self) -> &'static str {
|
||||
match self { Self::Local { .. } => "local", Self::S3 { .. } => "s3" }
|
||||
match self {
|
||||
Self::Local { .. } => "local",
|
||||
Self::S3 { .. } => "s3",
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn exists(&self, key: &str) -> Result<bool, StorageError> {
|
||||
match self {
|
||||
Self::Local { root } => Ok(root.join(key).is_file()),
|
||||
Self::S3 { client, bucket } => match client.head_object().bucket(bucket.as_ref()).key(key).send().await {
|
||||
Self::S3 { client, bucket } => match client
|
||||
.head_object()
|
||||
.bucket(bucket.as_ref())
|
||||
.key(key)
|
||||
.send()
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(true),
|
||||
Err(error) if error.as_service_error().is_some_and(|service| service.is_not_found()) => Ok(false),
|
||||
Err(error)
|
||||
if error
|
||||
.as_service_error()
|
||||
.is_some_and(|service| service.is_not_found()) =>
|
||||
{
|
||||
Ok(false)
|
||||
}
|
||||
Err(error) => Err(StorageError::Backend(error.to_string())),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn put(&self, key: &str, bytes: Bytes, content_type: &str, cache_control: &str) -> Result<(), StorageError> {
|
||||
pub async fn put(
|
||||
&self,
|
||||
key: &str,
|
||||
bytes: Bytes,
|
||||
content_type: &str,
|
||||
cache_control: &str,
|
||||
) -> Result<(), StorageError> {
|
||||
match self {
|
||||
Self::Local { root } => {
|
||||
let path = root.join(key);
|
||||
if let Some(parent) = path.parent() { tokio::fs::create_dir_all(parent).await?; }
|
||||
if let Some(parent) = path.parent() {
|
||||
tokio::fs::create_dir_all(parent).await?;
|
||||
}
|
||||
tokio::fs::write(path, bytes).await?;
|
||||
Ok(())
|
||||
}
|
||||
Self::S3 { client, bucket } => {
|
||||
client.put_object()
|
||||
client
|
||||
.put_object()
|
||||
.bucket(bucket.as_ref())
|
||||
.key(key)
|
||||
.content_type(content_type)
|
||||
.cache_control(cache_control)
|
||||
.body(ByteStream::from(bytes))
|
||||
.send().await
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| StorageError::Backend(error.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -99,9 +137,17 @@ impl Storage {
|
||||
match self {
|
||||
Self::Local { root } => Ok(Bytes::from(tokio::fs::read(root.join(key)).await?)),
|
||||
Self::S3 { client, bucket } => {
|
||||
let output = client.get_object().bucket(bucket.as_ref()).key(key).send().await
|
||||
let output = client
|
||||
.get_object()
|
||||
.bucket(bucket.as_ref())
|
||||
.key(key)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| StorageError::Backend(error.to_string()))?;
|
||||
let bytes = output.body.collect().await
|
||||
let bytes = output
|
||||
.body
|
||||
.collect()
|
||||
.await
|
||||
.map_err(|error| StorageError::Backend(error.to_string()))?
|
||||
.into_bytes();
|
||||
Ok(bytes)
|
||||
@@ -109,11 +155,19 @@ impl Storage {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_local_with_legacy(&self, key: &str, legacy_key: &str) -> Result<Bytes, StorageError> {
|
||||
pub async fn get_local_with_legacy(
|
||||
&self,
|
||||
key: &str,
|
||||
legacy_key: &str,
|
||||
) -> Result<Bytes, StorageError> {
|
||||
match self {
|
||||
Self::Local { root } => {
|
||||
let canonical = root.join(key);
|
||||
let path = if canonical.is_file() { canonical } else { root.join(legacy_key) };
|
||||
let path = if canonical.is_file() {
|
||||
canonical
|
||||
} else {
|
||||
root.join(legacy_key)
|
||||
};
|
||||
Ok(Bytes::from(tokio::fs::read(path).await?))
|
||||
}
|
||||
Self::S3 { .. } => self.get(key).await,
|
||||
@@ -128,7 +182,12 @@ impl Storage {
|
||||
Err(error) => Err(error.into()),
|
||||
},
|
||||
Self::S3 { client, bucket } => {
|
||||
client.delete_object().bucket(bucket.as_ref()).key(key).send().await
|
||||
client
|
||||
.delete_object()
|
||||
.bucket(bucket.as_ref())
|
||||
.key(key)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| StorageError::Backend(error.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -144,11 +203,18 @@ pub enum StorageError {
|
||||
|
||||
impl std::fmt::Display for StorageError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self { Self::Io(error) => write!(f, "{error}"), Self::Backend(error) => f.write_str(error) }
|
||||
match self {
|
||||
Self::Io(error) => write!(f, "{error}"),
|
||||
Self::Backend(error) => f.write_str(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl std::error::Error for StorageError {}
|
||||
impl From<std::io::Error> for StorageError { fn from(value: std::io::Error) -> Self { Self::Io(value) } }
|
||||
impl From<std::io::Error> for StorageError {
|
||||
fn from(value: std::io::Error) -> Self {
|
||||
Self::Io(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn object_key(kind: &str, owner_id: i64, token: &str, filename: &str) -> String {
|
||||
format!("{kind}/{owner_id}_{token}/{filename}")
|
||||
|
||||
Reference in New Issue
Block a user