feat: add media file retrieval functionality and update dependencies

This commit is contained in:
2025-11-02 16:22:09 +01:00
parent 4427428cf6
commit 596313b8c5
9 changed files with 205 additions and 7 deletions

View File

@@ -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"] }

View File

@@ -46,5 +46,6 @@ pub async fn build_app_state(config: Config) -> CoreResult<AppState> {
album_service,
token_generator: tokenizer,
nats_client,
config,
})
}

View File

@@ -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),
)))
})
}

View File

@@ -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?;

View File

@@ -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)
}
}

View File

@@ -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,
}