feat: add media file retrieval functionality and update dependencies
This commit is contained in:
128
Cargo.lock
generated
128
Cargo.lock
generated
@@ -907,6 +907,12 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range-header"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.10.1"
|
||||
@@ -1180,6 +1186,10 @@ dependencies = [
|
||||
"sha2",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
@@ -1276,6 +1286,15 @@ version = "0.4.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||
dependencies = [
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.8.4"
|
||||
@@ -1304,6 +1323,16 @@ version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
@@ -1380,6 +1409,15 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nuid"
|
||||
version = "0.5.0"
|
||||
@@ -2048,6 +2086,15 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
@@ -2428,6 +2475,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.44"
|
||||
@@ -2535,9 +2591,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.16"
|
||||
version = "0.7.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
|
||||
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -2583,6 +2639,32 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"http-range-header",
|
||||
"httpdate",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.3"
|
||||
@@ -2625,6 +2707,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex-automata",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2643,6 +2755,12 @@ version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.18"
|
||||
@@ -2706,6 +2824,12 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
|
||||
9
compose.yml
Normal file
9
compose.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
services:
|
||||
nats:
|
||||
image: nats:alpine
|
||||
container_name: libertas_nats
|
||||
ports:
|
||||
- "4222:4222"
|
||||
- "6222:6222"
|
||||
- "8222:8222"
|
||||
restart: unless-stopped
|
||||
@@ -32,3 +32,7 @@ sha2 = "0.10.9"
|
||||
futures = "0.3.31"
|
||||
bytes = "1.10.1"
|
||||
async-nats = "0.44.2"
|
||||
tower = { version = "0.5.2", features = ["util"] }
|
||||
tower-http = { version = "0.6.6", features = ["fs", "trace"] }
|
||||
tracing = "0.1.41"
|
||||
tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
|
||||
|
||||
@@ -46,5 +46,6 @@ pub async fn build_app_state(config: Config) -> CoreResult<AppState> {
|
||||
album_service,
|
||||
token_generator: tokenizer,
|
||||
nats_client,
|
||||
config,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
use axum::{
|
||||
Router,
|
||||
extract::{DefaultBodyLimit, Multipart, State},
|
||||
extract::{DefaultBodyLimit, Multipart, Path, Request, State},
|
||||
http::StatusCode,
|
||||
response::Json,
|
||||
routing::post,
|
||||
response::{IntoResponse, Json},
|
||||
routing::{get, post},
|
||||
};
|
||||
use futures::TryStreamExt;
|
||||
use libertas_core::{error::CoreError, models::Media, schema::UploadMediaData};
|
||||
use serde::Serialize;
|
||||
use std::io;
|
||||
use std::{io, path::PathBuf};
|
||||
|
||||
use tower::ServiceExt;
|
||||
use tower_http::services::ServeFile;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{error::ApiError, middleware::auth::UserId, state::AppState};
|
||||
|
||||
@@ -36,6 +40,7 @@ impl From<Media> for MediaResponse {
|
||||
pub fn media_routes() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/", post(upload_media))
|
||||
.route("/{media_id}/file", get(get_media_file))
|
||||
.layer(DefaultBodyLimit::max(250 * 1024 * 1024))
|
||||
}
|
||||
|
||||
@@ -75,3 +80,27 @@ async fn upload_media(
|
||||
|
||||
Ok((StatusCode::CREATED, Json(media.into())))
|
||||
}
|
||||
|
||||
async fn get_media_file(
|
||||
State(state): State<AppState>,
|
||||
UserId(user_id): UserId,
|
||||
Path(media_id): Path<Uuid>,
|
||||
request: Request,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let storage_path = state
|
||||
.media_service
|
||||
.get_media_filepath(media_id, user_id)
|
||||
.await?;
|
||||
|
||||
let full_path = PathBuf::from(&state.config.media_library_path).join(&storage_path);
|
||||
|
||||
ServeFile::new(full_path)
|
||||
.oneshot(request)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
ApiError::from(CoreError::Io(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
format!("File not found: {}", e),
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
pub mod factory;
|
||||
@@ -13,6 +15,16 @@ pub mod state;
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let config = config::load_config()?;
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
|
||||
format!("{}=debug,tower_http=debug", env!("CARGO_CRATE_NAME")).into()
|
||||
}),
|
||||
)
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.init();
|
||||
|
||||
let addr: SocketAddr = config.server_address.parse()?;
|
||||
|
||||
let app_state = factory::build_app_state(config).await?;
|
||||
|
||||
@@ -124,4 +124,18 @@ impl MediaService for MediaServiceImpl {
|
||||
async fn list_user_media(&self, user_id: Uuid) -> CoreResult<Vec<Media>> {
|
||||
self.repo.list_by_user(user_id).await
|
||||
}
|
||||
|
||||
async fn get_media_filepath(&self, id: Uuid, user_id: Uuid) -> CoreResult<String> {
|
||||
let media = self
|
||||
.repo
|
||||
.find_by_id(id)
|
||||
.await?
|
||||
.ok_or(CoreError::NotFound("Media".to_string(), id))?;
|
||||
|
||||
if media.owner_id != user_id {
|
||||
return Err(CoreError::Auth("Access denied".to_string()));
|
||||
}
|
||||
|
||||
Ok(media.storage_path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use libertas_core::services::{AlbumService, MediaService, UserService};
|
||||
use libertas_core::{
|
||||
config::Config,
|
||||
services::{AlbumService, MediaService, UserService},
|
||||
};
|
||||
|
||||
use crate::security::TokenGenerator;
|
||||
|
||||
@@ -11,4 +14,5 @@ pub struct AppState {
|
||||
pub album_service: Arc<dyn AlbumService>,
|
||||
pub token_generator: Arc<dyn TokenGenerator>,
|
||||
pub nats_client: async_nats::Client,
|
||||
pub config: Config,
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ pub trait MediaService: Send + Sync {
|
||||
async fn upload_media(&self, data: UploadMediaData<'_>) -> CoreResult<Media>;
|
||||
async fn get_media_details(&self, id: Uuid, user_id: Uuid) -> CoreResult<Media>;
|
||||
async fn list_user_media(&self, user_id: Uuid) -> CoreResult<Vec<Media>>;
|
||||
async fn get_media_filepath(&self, id: Uuid, user_id: Uuid) -> CoreResult<String>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
Reference in New Issue
Block a user