Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I9f113e6402030c46ad97f636985b5d6c6a6a6964
136 lines
3.3 KiB
Rust
136 lines
3.3 KiB
Rust
use axum::{
|
|
Json,
|
|
extract::{Path, State},
|
|
};
|
|
use pinakes_core::model::{CollectionKind, MediaId};
|
|
use uuid::Uuid;
|
|
|
|
use crate::{
|
|
dto::{
|
|
AddMemberRequest,
|
|
CollectionResponse,
|
|
CreateCollectionRequest,
|
|
MediaResponse,
|
|
},
|
|
error::ApiError,
|
|
state::AppState,
|
|
};
|
|
|
|
pub async fn create_collection(
|
|
State(state): State<AppState>,
|
|
Json(req): Json<CreateCollectionRequest>,
|
|
) -> Result<Json<CollectionResponse>, ApiError> {
|
|
if req.name.is_empty() || req.name.len() > 255 {
|
|
return Err(ApiError(
|
|
pinakes_core::error::PinakesError::InvalidOperation(
|
|
"collection name must be 1-255 characters".into(),
|
|
),
|
|
));
|
|
}
|
|
if let Some(ref desc) = req.description
|
|
&& desc.len() > 10_000
|
|
{
|
|
return Err(ApiError(
|
|
pinakes_core::error::PinakesError::InvalidOperation(
|
|
"description exceeds 10000 characters".into(),
|
|
),
|
|
));
|
|
}
|
|
let kind = match req.kind.as_str() {
|
|
"virtual" => CollectionKind::Virtual,
|
|
_ => CollectionKind::Manual,
|
|
};
|
|
let col = pinakes_core::collections::create_collection(
|
|
&state.storage,
|
|
&req.name,
|
|
kind,
|
|
req.description.as_deref(),
|
|
req.filter_query.as_deref(),
|
|
)
|
|
.await?;
|
|
|
|
state.emit_plugin_event(
|
|
"CollectionCreated",
|
|
&serde_json::json!({
|
|
"id": col.id.to_string(),
|
|
"name": col.name,
|
|
}),
|
|
);
|
|
|
|
Ok(Json(CollectionResponse::from(col)))
|
|
}
|
|
|
|
pub async fn list_collections(
|
|
State(state): State<AppState>,
|
|
) -> Result<Json<Vec<CollectionResponse>>, ApiError> {
|
|
let cols = state.storage.list_collections().await?;
|
|
Ok(Json(
|
|
cols.into_iter().map(CollectionResponse::from).collect(),
|
|
))
|
|
}
|
|
|
|
pub async fn get_collection(
|
|
State(state): State<AppState>,
|
|
Path(id): Path<Uuid>,
|
|
) -> Result<Json<CollectionResponse>, ApiError> {
|
|
let col = state.storage.get_collection(id).await?;
|
|
Ok(Json(CollectionResponse::from(col)))
|
|
}
|
|
|
|
pub async fn delete_collection(
|
|
State(state): State<AppState>,
|
|
Path(id): Path<Uuid>,
|
|
) -> Result<Json<serde_json::Value>, ApiError> {
|
|
state.storage.delete_collection(id).await?;
|
|
|
|
state.emit_plugin_event(
|
|
"CollectionDeleted",
|
|
&serde_json::json!({"id": id.to_string()}),
|
|
);
|
|
|
|
Ok(Json(serde_json::json!({"deleted": true})))
|
|
}
|
|
|
|
pub async fn add_member(
|
|
State(state): State<AppState>,
|
|
Path(collection_id): Path<Uuid>,
|
|
Json(req): Json<AddMemberRequest>,
|
|
) -> Result<Json<serde_json::Value>, ApiError> {
|
|
pinakes_core::collections::add_member(
|
|
&state.storage,
|
|
collection_id,
|
|
MediaId(req.media_id),
|
|
req.position.unwrap_or(0),
|
|
)
|
|
.await?;
|
|
Ok(Json(serde_json::json!({"added": true})))
|
|
}
|
|
|
|
pub async fn remove_member(
|
|
State(state): State<AppState>,
|
|
Path((collection_id, media_id)): Path<(Uuid, Uuid)>,
|
|
) -> Result<Json<serde_json::Value>, ApiError> {
|
|
pinakes_core::collections::remove_member(
|
|
&state.storage,
|
|
collection_id,
|
|
MediaId(media_id),
|
|
)
|
|
.await?;
|
|
Ok(Json(serde_json::json!({"removed": true})))
|
|
}
|
|
|
|
pub async fn get_members(
|
|
State(state): State<AppState>,
|
|
Path(collection_id): Path<Uuid>,
|
|
) -> Result<Json<Vec<MediaResponse>>, ApiError> {
|
|
let items =
|
|
pinakes_core::collections::get_members(&state.storage, collection_id)
|
|
.await?;
|
|
let roots = state.config.read().await.directories.roots.clone();
|
|
Ok(Json(
|
|
items
|
|
.into_iter()
|
|
.map(|item| MediaResponse::new(item, &roots))
|
|
.collect(),
|
|
))
|
|
}
|