cleanup code

This commit is contained in:
Mateusz Gruszczyński
2026-07-24 11:22:44 +02:00
parent f52cf91470
commit 33b4667e42
12 changed files with 2387 additions and 712 deletions
+85 -19
View File
@@ -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}")