treewide: fix various UI bugs; optimize crypto dependencies & format
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: If8fe8b38c1d9c4fecd40ff71f88d2ae06a6a6964
This commit is contained in:
parent
764aafa88d
commit
3ccddce7fd
178 changed files with 58342 additions and 54241 deletions
|
|
@ -1,120 +1,97 @@
|
|||
use axum::{
|
||||
Json,
|
||||
extract::{Multipart, Path, State},
|
||||
http::{StatusCode, header},
|
||||
response::IntoResponse,
|
||||
Json,
|
||||
extract::{Multipart, Path, State},
|
||||
http::{StatusCode, header},
|
||||
response::IntoResponse,
|
||||
};
|
||||
use pinakes_core::{model::MediaId, upload};
|
||||
use tokio_util::io::ReaderStream;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::dto::{ManagedStorageStatsResponse, UploadResponse};
|
||||
use crate::error::{ApiError, ApiResult};
|
||||
use crate::state::AppState;
|
||||
use pinakes_core::model::MediaId;
|
||||
use pinakes_core::upload;
|
||||
use crate::{
|
||||
dto::{ManagedStorageStatsResponse, UploadResponse},
|
||||
error::{ApiError, ApiResult},
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
/// Upload a file to managed storage
|
||||
/// POST /api/upload
|
||||
pub async fn upload_file(
|
||||
State(state): State<AppState>,
|
||||
mut multipart: Multipart,
|
||||
State(state): State<AppState>,
|
||||
mut multipart: Multipart,
|
||||
) -> ApiResult<Json<UploadResponse>> {
|
||||
let managed_storage = state
|
||||
.managed_storage
|
||||
.as_ref()
|
||||
.ok_or_else(|| ApiError::bad_request("Managed storage is not enabled"))?;
|
||||
let managed_storage = state
|
||||
.managed_storage
|
||||
.as_ref()
|
||||
.ok_or_else(|| ApiError::bad_request("Managed storage is not enabled"))?;
|
||||
|
||||
let config = state.config.read().await;
|
||||
if !config.managed_storage.enabled {
|
||||
return Err(ApiError::bad_request("Managed storage is not enabled"));
|
||||
}
|
||||
drop(config);
|
||||
let config = state.config.read().await;
|
||||
if !config.managed_storage.enabled {
|
||||
return Err(ApiError::bad_request("Managed storage is not enabled"));
|
||||
}
|
||||
drop(config);
|
||||
|
||||
// Extract file from multipart
|
||||
let field = multipart
|
||||
.next_field()
|
||||
.await
|
||||
.map_err(|e| ApiError::bad_request(format!("Failed to read multipart field: {}", e)))?
|
||||
.ok_or_else(|| ApiError::bad_request("No file provided"))?;
|
||||
|
||||
let original_filename = field
|
||||
.file_name()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
|
||||
let content_type = field
|
||||
.content_type()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| "application/octet-stream".to_string());
|
||||
|
||||
let data = field
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|e| ApiError::bad_request(format!("Failed to read file data: {}", e)))?;
|
||||
|
||||
// Process the upload
|
||||
let result = upload::process_upload_bytes(
|
||||
&state.storage,
|
||||
managed_storage.as_ref(),
|
||||
&data,
|
||||
&original_filename,
|
||||
Some(&content_type),
|
||||
)
|
||||
// Extract file from multipart
|
||||
let field = multipart
|
||||
.next_field()
|
||||
.await
|
||||
.map_err(|e| ApiError::internal(format!("Upload failed: {}", e)))?;
|
||||
.map_err(|e| {
|
||||
ApiError::bad_request(format!("Failed to read multipart field: {}", e))
|
||||
})?
|
||||
.ok_or_else(|| ApiError::bad_request("No file provided"))?;
|
||||
|
||||
Ok(Json(result.into()))
|
||||
let original_filename = field
|
||||
.file_name()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
|
||||
let content_type = field
|
||||
.content_type()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| "application/octet-stream".to_string());
|
||||
|
||||
let data = field.bytes().await.map_err(|e| {
|
||||
ApiError::bad_request(format!("Failed to read file data: {}", e))
|
||||
})?;
|
||||
|
||||
// Process the upload
|
||||
let result = upload::process_upload_bytes(
|
||||
&state.storage,
|
||||
managed_storage.as_ref(),
|
||||
&data,
|
||||
&original_filename,
|
||||
Some(&content_type),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ApiError::internal(format!("Upload failed: {}", e)))?;
|
||||
|
||||
Ok(Json(result.into()))
|
||||
}
|
||||
|
||||
/// Download a managed file
|
||||
/// GET /api/media/{id}/download
|
||||
pub async fn download_file(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> ApiResult<impl IntoResponse> {
|
||||
let media_id = MediaId(id);
|
||||
let item = state
|
||||
.storage
|
||||
.get_media(media_id)
|
||||
.await
|
||||
.map_err(|e| ApiError::not_found(format!("Media not found: {}", e)))?;
|
||||
let media_id = MediaId(id);
|
||||
let item = state
|
||||
.storage
|
||||
.get_media(media_id)
|
||||
.await
|
||||
.map_err(|e| ApiError::not_found(format!("Media not found: {}", e)))?;
|
||||
|
||||
let managed_storage = state
|
||||
.managed_storage
|
||||
.as_ref()
|
||||
.ok_or_else(|| ApiError::bad_request("Managed storage is not enabled"))?;
|
||||
let managed_storage = state
|
||||
.managed_storage
|
||||
.as_ref()
|
||||
.ok_or_else(|| ApiError::bad_request("Managed storage is not enabled"))?;
|
||||
|
||||
// Check if this is a managed file
|
||||
if item.storage_mode != pinakes_core::model::StorageMode::Managed {
|
||||
// For external files, stream from their original path
|
||||
let file = tokio::fs::File::open(&item.path)
|
||||
.await
|
||||
.map_err(|e| ApiError::not_found(format!("File not found: {}", e)))?;
|
||||
|
||||
let stream = ReaderStream::new(file);
|
||||
let body = axum::body::Body::from_stream(stream);
|
||||
|
||||
let content_type = item.media_type.mime_type();
|
||||
|
||||
let filename = item.original_filename.unwrap_or(item.file_name);
|
||||
|
||||
return Ok((
|
||||
[
|
||||
(header::CONTENT_TYPE, content_type),
|
||||
(
|
||||
header::CONTENT_DISPOSITION,
|
||||
format!("attachment; filename=\"{}\"", filename),
|
||||
),
|
||||
],
|
||||
body,
|
||||
));
|
||||
}
|
||||
|
||||
// For managed files, stream from content-addressable storage
|
||||
let file = managed_storage
|
||||
.open(&item.content_hash)
|
||||
.await
|
||||
.map_err(|e| ApiError::not_found(format!("Blob not found: {}", e)))?;
|
||||
// Check if this is a managed file
|
||||
if item.storage_mode != pinakes_core::model::StorageMode::Managed {
|
||||
// For external files, stream from their original path
|
||||
let file = tokio::fs::File::open(&item.path)
|
||||
.await
|
||||
.map_err(|e| ApiError::not_found(format!("File not found: {}", e)))?;
|
||||
|
||||
let stream = ReaderStream::new(file);
|
||||
let body = axum::body::Body::from_stream(stream);
|
||||
|
|
@ -123,47 +100,76 @@ pub async fn download_file(
|
|||
|
||||
let filename = item.original_filename.unwrap_or(item.file_name);
|
||||
|
||||
Ok((
|
||||
[
|
||||
(header::CONTENT_TYPE, content_type),
|
||||
(
|
||||
header::CONTENT_DISPOSITION,
|
||||
format!("attachment; filename=\"{}\"", filename),
|
||||
),
|
||||
],
|
||||
body,
|
||||
))
|
||||
return Ok((
|
||||
[
|
||||
(header::CONTENT_TYPE, content_type),
|
||||
(
|
||||
header::CONTENT_DISPOSITION,
|
||||
format!("attachment; filename=\"{}\"", filename),
|
||||
),
|
||||
],
|
||||
body,
|
||||
));
|
||||
}
|
||||
|
||||
// For managed files, stream from content-addressable storage
|
||||
let file = managed_storage
|
||||
.open(&item.content_hash)
|
||||
.await
|
||||
.map_err(|e| ApiError::not_found(format!("Blob not found: {}", e)))?;
|
||||
|
||||
let stream = ReaderStream::new(file);
|
||||
let body = axum::body::Body::from_stream(stream);
|
||||
|
||||
let content_type = item.media_type.mime_type();
|
||||
|
||||
let filename = item.original_filename.unwrap_or(item.file_name);
|
||||
|
||||
Ok((
|
||||
[
|
||||
(header::CONTENT_TYPE, content_type),
|
||||
(
|
||||
header::CONTENT_DISPOSITION,
|
||||
format!("attachment; filename=\"{}\"", filename),
|
||||
),
|
||||
],
|
||||
body,
|
||||
))
|
||||
}
|
||||
|
||||
/// Migrate an external file to managed storage
|
||||
/// POST /api/media/{id}/move-to-managed
|
||||
pub async fn move_to_managed(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> ApiResult<StatusCode> {
|
||||
let managed_storage = state
|
||||
.managed_storage
|
||||
.as_ref()
|
||||
.ok_or_else(|| ApiError::bad_request("Managed storage is not enabled"))?;
|
||||
let managed_storage = state
|
||||
.managed_storage
|
||||
.as_ref()
|
||||
.ok_or_else(|| ApiError::bad_request("Managed storage is not enabled"))?;
|
||||
|
||||
let media_id = MediaId(id);
|
||||
upload::migrate_to_managed(&state.storage, managed_storage.as_ref(), media_id)
|
||||
.await
|
||||
.map_err(|e| ApiError::internal(format!("Migration failed: {}", e)))?;
|
||||
let media_id = MediaId(id);
|
||||
upload::migrate_to_managed(
|
||||
&state.storage,
|
||||
managed_storage.as_ref(),
|
||||
media_id,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ApiError::internal(format!("Migration failed: {}", e)))?;
|
||||
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
||||
/// Get managed storage statistics
|
||||
/// GET /api/managed/stats
|
||||
pub async fn managed_stats(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<AppState>,
|
||||
) -> ApiResult<Json<ManagedStorageStatsResponse>> {
|
||||
let stats = state
|
||||
.storage
|
||||
.managed_storage_stats()
|
||||
.await
|
||||
.map_err(|e| ApiError::internal(format!("Failed to get stats: {}", e)))?;
|
||||
let stats = state
|
||||
.storage
|
||||
.managed_storage_stats()
|
||||
.await
|
||||
.map_err(|e| ApiError::internal(format!("Failed to get stats: {}", e)))?;
|
||||
|
||||
Ok(Json(stats.into()))
|
||||
Ok(Json(stats.into()))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue