diff --git a/Cargo.lock b/Cargo.lock index 517ab17e1..6ee54e018 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,7 +75,7 @@ dependencies = [ "derive_more 2.1.1", "encoding_rs", "flate2", - "foldhash", + "foldhash 0.1.5", "futures-core", "h2 0.3.27", "http 0.2.12", @@ -228,7 +228,7 @@ dependencies = [ "cookie 0.16.2", "derive_more 2.1.1", "encoding_rs", - "foldhash", + "foldhash 0.1.5", "futures-core", "futures-util", "impl-more", @@ -914,6 +914,18 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "aws-credential-types" +version = "1.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd362783681b15d136480ad555a099e82ecd8e2d10a841e14dfd0078d67fee3" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + [[package]] name = "aws-creds" version = "0.39.0" @@ -964,6 +976,295 @@ dependencies = [ "thiserror 2.0.17", ] +[[package]] +name = "aws-runtime" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c635c2dc792cb4a11ce1a4f392a925340d1bdf499289b5ec1ec6810954eb43f5" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand 2.3.0", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "http-body 1.0.1", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid 1.18.1", +] + +[[package]] +name = "aws-sdk-s3" +version = "1.122.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94c2ca0cba97e8e279eb6c0b2d0aa10db5959000e602ab2b7c02de6b85d4c19b" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-checksums", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "fastrand 2.3.0", + "hex", + "hmac", + "http 0.2.12", + "http 1.3.1", + "http-body 1.0.1", + "lru", + "percent-encoding", + "regex-lite", + "sha2", + "tracing", + "url", +] + +[[package]] +name = "aws-sigv4" +version = "1.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa49f3c607b92daae0c078d48a4571f599f966dce3caee5f1ea55c4d9073f99" +dependencies = [ + "aws-credential-types", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.3.1", + "percent-encoding", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52eec3db979d18cb807fc1070961cc51d87d069abe9ab57917769687368a8c6c" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.64.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddcf418858f9f3edd228acb8759d77394fed7531cce78d02bdda499025368439" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "crc-fast", + "hex", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.60.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b9c7354a3b13c66f60fe4616d6d1969c9fd36b1b5333a5dfb3ee716b33c588" +dependencies = [ + "aws-smithy-types", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.63.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630e67f2a31094ffa51b210ae030855cb8f3b7ee1329bdd8d085aaf61e8b97fc" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-http-client" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12fb0abf49ff0cab20fd31ac1215ed7ce0ea92286ba09e2854b42ba5cabe7525" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "h2 0.3.27", + "h2 0.4.12", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper 1.7.0", + "hyper-rustls 0.24.2", + "hyper-rustls 0.27.7", + "hyper-util", + "pin-project-lite", + "rustls 0.21.12", + "rustls 0.23.32", + "rustls-native-certs 0.8.1", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.4", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.62.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb96aa208d62ee94104645f7b2ecaf77bf27edf161590b6224bfbac2832f979" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-observability" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0a46543fbc94621080b3cf553eb4cbbdc41dd9780a30c4756400f0139440a1d" +dependencies = [ + "aws-smithy-runtime-api", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3df87c14f0127a0d77eb261c3bc45d5b4833e2a1f63583ebfb728e4852134ee" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-http-client", + "aws-smithy-observability", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand 2.3.0", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", + "pin-project-lite", + "pin-utils", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49952c52f7eebb72ce2a754d3866cc0f87b97d2a46146b79f80f3a93fb2b3716" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.3.1", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-types" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3a26048eeab0ddeba4b4f9d51654c79af8c3b32357dc5f336cee85ab331c33" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11b2f670422ff42bf7065031e72b45bc52a3508bd089f743ea90731ca2b6ea57" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d980627d2dd7bfc32a3c025685a033eeab8d365cc840c631ef59d1b8f428164" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "rustc_version", + "tracing", +] + [[package]] name = "axum" version = "0.8.8" @@ -1052,6 +1353,16 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + [[package]] name = "base64ct" version = "1.8.0" @@ -1067,7 +1378,7 @@ dependencies = [ "bitflags 2.9.4", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.12.1", "log", "prettyplease", "proc-macro2", @@ -1372,6 +1683,16 @@ dependencies = [ "serde", ] +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + [[package]] name = "bytestring" version = "1.5.0" @@ -2025,6 +2346,18 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crc-fast" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd92aca2c6001b1bf5ba0ff84ee74ec8501b52bbef0cac80bf25a6c1d87a83d" +dependencies = [ + "crc", + "digest", + "rustversion", + "spin 0.10.0", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -2593,7 +2926,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.8", + "libloading 0.7.4", ] [[package]] @@ -2949,7 +3282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3150,7 +3483,7 @@ checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", - "spin", + "spin 0.9.8", ] [[package]] @@ -3165,6 +3498,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -3816,7 +4155,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -3824,6 +4163,11 @@ name = "hashbrown" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "hashlink" @@ -4255,7 +4599,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -4592,9 +4936,9 @@ dependencies = [ [[package]] name = "io-uring" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +checksum = "4d09b98f7eace8982db770e4408e7470b028ce513ac28fecdc6bf4c30fe92b62" dependencies = [ "bitflags 2.9.4", "cfg-if", @@ -4652,7 +4996,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi 0.5.2", "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4913,6 +5257,7 @@ dependencies = [ "async-minecraft-ping", "async-stripe", "async-trait", + "aws-sdk-s3", "base64 0.22.1", "bitflags 2.9.4", "bytes", @@ -4958,7 +5303,6 @@ dependencies = [ "redis", "regex", "reqwest 0.12.24", - "rust-s3", "rust_decimal", "rust_iso3166", "rustls 0.23.32", @@ -5017,7 +5361,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin", + "spin 0.9.8", ] [[package]] @@ -5118,7 +5462,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.53.5", + "windows-targets 0.48.5", ] [[package]] @@ -5239,6 +5583,15 @@ dependencies = [ "imgref", ] +[[package]] +name = "lru" +version = "0.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" +dependencies = [ + "hashbrown 0.16.0", +] + [[package]] name = "lru-cache" version = "0.1.2" @@ -6480,6 +6833,12 @@ dependencies = [ "thiserror 2.0.17", ] +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + [[package]] name = "owo-colors" version = "4.2.3" @@ -7365,7 +7724,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls 0.23.32", - "socket2 0.6.1", + "socket2 0.5.10", "thiserror 2.0.17", "tokio", "tracing", @@ -7402,9 +7761,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.5.10", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -8077,7 +8436,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -8090,7 +8449,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -9071,6 +9430,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" + [[package]] name = "spki" version = "0.7.3" @@ -9312,6 +9677,7 @@ dependencies = [ "cfg-if", "libc", "psm", + "windows-sys 0.52.0", "windows-sys 0.59.0", ] @@ -10076,7 +10442,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix 1.1.2", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -11304,6 +11670,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "vswhom" version = "0.1.0" @@ -11724,7 +12096,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] @@ -12370,6 +12742,12 @@ version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + [[package]] name = "yaserde" version = "0.12.0" diff --git a/Cargo.toml b/Cargo.toml index 0e9a5ad67..2f5ec3ef1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,11 @@ async-tungstenite = { version = "0.31.0", default-features = false, features = [ ] } async-walkdir = "2.1.0" async_zip = "0.0.18" +aws-sdk-s3 = { version = "=1.122.0", default-features = false, features = [ + "default-https-client", + "rt-tokio", + "rustls", +] } base64 = "0.22.1" bitflags = "2.9.4" bytemuck = "1.24.0" diff --git a/apps/labrinth/Cargo.toml b/apps/labrinth/Cargo.toml index 40639cd44..0632770a0 100644 --- a/apps/labrinth/Cargo.toml +++ b/apps/labrinth/Cargo.toml @@ -29,6 +29,7 @@ async-stripe = { workspace = true, features = [ "webhook-events" ] } async-trait = { workspace = true } +aws-sdk-s3 = { workspace = true } base64 = { workspace = true } bitflags = { workspace = true } bytes = { workspace = true } @@ -96,7 +97,6 @@ rust_decimal = { workspace = true, features = [ "serde-with-str" ] } rust_iso3166 = { workspace = true } -rust-s3 = { workspace = true } rustls.workspace = true rusty-money = { workspace = true } sentry = { workspace = true } diff --git a/apps/labrinth/src/file_hosting/mod.rs b/apps/labrinth/src/file_hosting/mod.rs index 2a930a32a..667f4cb21 100644 --- a/apps/labrinth/src/file_hosting/mod.rs +++ b/apps/labrinth/src/file_hosting/mod.rs @@ -1,3 +1,4 @@ +use std::error::Error; use std::str::FromStr; use async_trait::async_trait; @@ -13,7 +14,7 @@ pub use s3_host::{S3BucketConfig, S3Host}; #[derive(Error, Debug)] pub enum FileHostingError { #[error("S3 error when {0}: {1}")] - S3Error(&'static str, s3::error::S3Error), + S3Error(&'static str, #[source] Box), #[error("File system error in file hosting: {0}")] FileSystemError(#[from] std::io::Error), #[error("Invalid Filename")] diff --git a/apps/labrinth/src/file_hosting/s3_host.rs b/apps/labrinth/src/file_hosting/s3_host.rs index a1a7c02df..56bd0ef45 100644 --- a/apps/labrinth/src/file_hosting/s3_host.rs +++ b/apps/labrinth/src/file_hosting/s3_host.rs @@ -3,13 +3,16 @@ use crate::file_hosting::{ UploadFileData, }; use async_trait::async_trait; +use aws_sdk_s3::Client; +use aws_sdk_s3::config::{BehaviorVersion, Credentials, Region}; +use aws_sdk_s3::presigning::PresigningConfig; +use aws_sdk_s3::primitives::ByteStream; use bytes::Bytes; use chrono::Utc; use hex::ToHex; -use s3::bucket::Bucket; -use s3::creds::Credentials; -use s3::region::Region; use sha2::Digest; +use std::error::Error; +use std::time::Duration; pub struct S3BucketConfig { pub name: String, @@ -21,8 +24,13 @@ pub struct S3BucketConfig { } pub struct S3Host { - public_bucket: Bucket, - private_bucket: Bucket, + public_bucket: S3Bucket, + private_bucket: S3Bucket, +} + +struct S3Bucket { + name: String, + client: Client, } impl S3Host { @@ -30,47 +38,45 @@ impl S3Host { public_bucket: S3BucketConfig, private_bucket: S3BucketConfig, ) -> Result { - let create_bucket = - |config: S3BucketConfig| -> Result<_, FileHostingError> { - let mut bucket = Bucket::new( - "", - if config.region == "r2" { - Region::R2 { - account_id: config.url, - } - } else { - Region::Custom { - region: config.region, - endpoint: config.url, - } - }, - Credentials { - access_key: Some(config.access_token), - secret_key: Some(config.secret), - ..Credentials::anonymous().unwrap() - }, + let create_bucket = |config: S3BucketConfig| -> S3Bucket { + let (region, endpoint_url, provider_name) = if config.region == "r2" + { + ( + "auto".to_string(), + format!("https://{}.r2.cloudflarestorage.com", config.url), + "R2", ) - .map_err(|e| { - FileHostingError::S3Error("creating Bucket instance", e) - })?; - - bucket.name = config.name; - if config.uses_path_style { - bucket.set_path_style(); - } else { - bucket.set_subdomain_style(); - } - - Ok(bucket) + } else { + (config.region, config.url, "Labrinth") }; + let s3_config = aws_sdk_s3::config::Builder::new() + .behavior_version(BehaviorVersion::latest()) + .region(Region::new(region)) + .endpoint_url(endpoint_url) + .credentials_provider(Credentials::new( + config.access_token, + config.secret, + None, + None, + provider_name, + )) + .force_path_style(config.uses_path_style) + .build(); + + S3Bucket { + name: config.name, + client: Client::from_conf(s3_config), + } + }; + Ok(S3Host { - public_bucket: *create_bucket(public_bucket)?, - private_bucket: *create_bucket(private_bucket)?, + public_bucket: create_bucket(public_bucket), + private_bucket: create_bucket(private_bucket), }) } - fn get_bucket(&self, publicity: FileHostPublicity) -> &Bucket { + fn get_bucket(&self, publicity: FileHostPublicity) -> &S3Bucket { match publicity { FileHostPublicity::Public => &self.public_bucket, FileHostPublicity::Private => &self.private_bucket, @@ -78,6 +84,13 @@ impl S3Host { } } +fn s3_error( + context: &'static str, + error: impl Error + Send + Sync + 'static, +) -> FileHostingError { + FileHostingError::S3Error(context, Box::new(error)) +} + #[async_trait] impl FileHost for S3Host { async fn upload_file( @@ -89,20 +102,24 @@ impl FileHost for S3Host { ) -> Result { let content_sha1 = sha1::Sha1::digest(&file_bytes).encode_hex(); let content_sha512 = format!("{:x}", sha2::Sha512::digest(&file_bytes)); + let content_length = file_bytes.len() as u32; + let bucket = self.get_bucket(file_publicity); - self.get_bucket(file_publicity) - .put_object_with_content_type( - format!("/{file_name}"), - &file_bytes, - content_type, - ) + bucket + .client + .put_object() + .bucket(bucket.name.as_str()) + .key(file_name) + .content_type(content_type) + .body(ByteStream::from(file_bytes)) + .send() .await - .map_err(|e| FileHostingError::S3Error("uploading file", e))?; + .map_err(|e| s3_error("uploading file", e))?; Ok(UploadFileData { file_name: file_name.to_string(), file_publicity, - content_length: file_bytes.len() as u32, + content_length, content_sha512, content_sha1, content_md5: None, @@ -116,14 +133,20 @@ impl FileHost for S3Host { file_name: &str, expiry_secs: u32, ) -> Result { + let presigning_config = PresigningConfig::expires_in( + Duration::from_secs(expiry_secs.into()), + ) + .map_err(|e| s3_error("creating presigning config", e))?; let url = self .private_bucket - .presign_get(format!("/{file_name}"), expiry_secs, None) + .client + .get_object() + .bucket(self.private_bucket.name.as_str()) + .key(file_name) + .presigned(presigning_config) .await - .map_err(|e| { - FileHostingError::S3Error("generating presigned URL", e) - })?; - Ok(url) + .map_err(|e| s3_error("generating presigned URL", e))?; + Ok(url.uri().to_string()) } async fn delete_file( @@ -131,10 +154,16 @@ impl FileHost for S3Host { file_name: &str, file_publicity: FileHostPublicity, ) -> Result { - self.get_bucket(file_publicity) - .delete_object(format!("/{file_name}")) + let bucket = self.get_bucket(file_publicity); + + bucket + .client + .delete_object() + .bucket(bucket.name.as_str()) + .key(file_name) + .send() .await - .map_err(|e| FileHostingError::S3Error("deleting file", e))?; + .map_err(|e| s3_error("deleting file", e))?; Ok(DeleteFileData { file_name: file_name.to_string(),