Add (untested) get_url_for_private_file

This commit is contained in:
Josiah Glosson 2025-06-11 12:41:57 -05:00
parent 3e6a05cd04
commit 9e1f613aae
5 changed files with 50 additions and 25 deletions

View File

@ -9,7 +9,7 @@ use ariadne::ids::DecodingError;
#[error("{}", .error_type)]
pub struct OAuthError {
#[source]
pub error_type: OAuthErrorType,
pub error_type: Box<OAuthErrorType>,
pub state: Option<String>,
pub valid_redirect_uri: Option<ValidatedRedirectUri>,
@ -32,7 +32,7 @@ impl OAuthError {
/// See: IETF RFC 6749 4.1.2.1 (https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1)
pub fn error(error_type: impl Into<OAuthErrorType>) -> Self {
Self {
error_type: error_type.into(),
error_type: Box::new(error_type.into()),
valid_redirect_uri: None,
state: None,
}
@ -48,7 +48,7 @@ impl OAuthError {
valid_redirect_uri: &ValidatedRedirectUri,
) -> Self {
Self {
error_type: err.into(),
error_type: Box::new(err.into()),
state: state.clone(),
valid_redirect_uri: Some(valid_redirect_uri.clone()),
}
@ -57,7 +57,7 @@ impl OAuthError {
impl actix_web::ResponseError for OAuthError {
fn status_code(&self) -> StatusCode {
match self.error_type {
match *self.error_type {
OAuthErrorType::AuthenticationError(_)
| OAuthErrorType::FailedScopeParse(_)
| OAuthErrorType::ScopesTooBroad

View File

@ -101,7 +101,7 @@ mod tests {
);
assert!(validated.is_err_and(|e| matches!(
e.error_type,
*e.error_type,
OAuthErrorType::RedirectUriNotConfigured(_)
)));
}

View File

@ -8,6 +8,7 @@ use chrono::Utc;
use hex::ToHex;
use sha2::Digest;
use std::path::PathBuf;
use std::time::Duration;
#[derive(Default)]
pub struct MockHost(());
@ -47,6 +48,15 @@ impl FileHost for MockHost {
})
}
async fn get_url_for_private_file(
&self,
file_name: &str,
_expiry: Duration,
) -> Result<String, FileHostingError> {
let cdn_url = dotenvy::var("CDN_URL").unwrap();
Ok(format!("{cdn_url}/private/{file_name}"))
}
async fn delete_file(
&self,
file_name: &str,

View File

@ -1,4 +1,5 @@
use async_trait::async_trait;
use std::time::Duration;
use thiserror::Error;
mod mock;
@ -10,8 +11,8 @@ pub use s3_host::S3Host;
#[derive(Error, Debug)]
pub enum FileHostingError {
#[error("S3 error: {0}")]
S3Error(String),
#[error("S3 error when {0}: {1}")]
S3Error(&'static str, s3::error::S3Error),
#[error("File system error in file hosting: {0}")]
FileSystemError(#[from] std::io::Error),
#[error("Invalid Filename")]
@ -51,6 +52,12 @@ pub trait FileHost {
file_bytes: Bytes,
) -> Result<UploadFileData, FileHostingError>;
async fn get_url_for_private_file(
&self,
file_name: &str,
expiry: Duration,
) -> Result<String, FileHostingError>;
async fn delete_file(
&self,
file_name: &str,

View File

@ -10,6 +10,7 @@ use s3::bucket::Bucket;
use s3::creds::Credentials;
use s3::region::Region;
use sha2::Digest;
use std::time::Duration;
pub struct S3Host {
public_bucket: Bucket,
@ -46,16 +47,12 @@ impl S3Host {
None,
None,
)
.map_err(|_| {
FileHostingError::S3Error(
"Error while creating credentials".to_string(),
)
.map_err(|e| {
FileHostingError::S3Error("creating credentials", e.into())
})?,
)
.map_err(|_| {
FileHostingError::S3Error(
"Error while creating Bucket instance".to_string(),
)
.map_err(|e| {
FileHostingError::S3Error("creating Bucket instance", e)
})?;
if bucket_uses_path_style {
@ -100,11 +97,7 @@ impl FileHost for S3Host {
content_type,
)
.await
.map_err(|err| {
FileHostingError::S3Error(format!(
"Error while uploading file {file_name} to S3: {err}"
))
})?;
.map_err(|e| FileHostingError::S3Error("uploading file", e))?;
Ok(UploadFileData {
file_name: file_name.to_string(),
@ -118,6 +111,25 @@ impl FileHost for S3Host {
})
}
async fn get_url_for_private_file(
&self,
file_name: &str,
expiry: Duration,
) -> Result<String, FileHostingError> {
let url = self
.private_bucket
.presign_get(
format!("/{file_name}"),
expiry.as_secs().try_into().unwrap(),
None,
)
.await
.map_err(|e| {
FileHostingError::S3Error("generating presigned URL", e)
})?;
Ok(url)
}
async fn delete_file(
&self,
file_name: &str,
@ -126,11 +138,7 @@ impl FileHost for S3Host {
self.get_bucket(file_publicity)
.delete_object(format!("/{file_name}"))
.await
.map_err(|err| {
FileHostingError::S3Error(format!(
"Error while deleting file {file_name} to S3: {err}"
))
})?;
.map_err(|e| FileHostingError::S3Error("deleting file", e))?;
Ok(DeleteFileData {
file_name: file_name.to_string(),