Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I928162008cb1ba02e1aa0e7aa971e8326a6a6964
150 lines
3.9 KiB
Rust
150 lines
3.9 KiB
Rust
use axum::{
|
|
Json,
|
|
extract::{Path, State},
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{error::ApiError, state::AppState};
|
|
|
|
#[derive(Debug, Deserialize, utoipa::ToSchema)]
|
|
pub struct CreateSavedSearchRequest {
|
|
pub name: String,
|
|
pub query: String,
|
|
pub sort_order: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, utoipa::ToSchema)]
|
|
pub struct SavedSearchResponse {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub query: String,
|
|
pub sort_order: Option<String>,
|
|
pub created_at: chrono::DateTime<chrono::Utc>,
|
|
}
|
|
|
|
const VALID_SORT_ORDERS: &[&str] = &[
|
|
"date_asc",
|
|
"date_desc",
|
|
"name_asc",
|
|
"name_desc",
|
|
"size_asc",
|
|
"size_desc",
|
|
];
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/v1/searches",
|
|
tag = "saved_searches",
|
|
request_body = CreateSavedSearchRequest,
|
|
responses(
|
|
(status = 200, description = "Search saved", body = SavedSearchResponse),
|
|
(status = 400, description = "Bad request"),
|
|
(status = 401, description = "Unauthorized"),
|
|
(status = 500, description = "Internal server error"),
|
|
),
|
|
security(("bearer_auth" = []))
|
|
)]
|
|
pub async fn create_saved_search(
|
|
State(state): State<AppState>,
|
|
Json(req): Json<CreateSavedSearchRequest>,
|
|
) -> Result<Json<SavedSearchResponse>, ApiError> {
|
|
let name_len = req.name.chars().count();
|
|
if name_len == 0 || name_len > 255 {
|
|
return Err(ApiError(
|
|
pinakes_core::error::PinakesError::InvalidOperation(
|
|
"name must be 1-255 characters".into(),
|
|
),
|
|
));
|
|
}
|
|
if req.query.is_empty() || req.query.len() > 2048 {
|
|
return Err(ApiError(
|
|
pinakes_core::error::PinakesError::InvalidOperation(
|
|
"query must be 1-2048 bytes".into(),
|
|
),
|
|
));
|
|
}
|
|
if let Some(ref sort) = req.sort_order
|
|
&& !VALID_SORT_ORDERS.contains(&sort.as_str())
|
|
{
|
|
return Err(ApiError(
|
|
pinakes_core::error::PinakesError::InvalidOperation(format!(
|
|
"sort_order must be one of: {}",
|
|
VALID_SORT_ORDERS.join(", ")
|
|
)),
|
|
));
|
|
}
|
|
let id = uuid::Uuid::now_v7();
|
|
state
|
|
.storage
|
|
.save_search(id, &req.name, &req.query, req.sort_order.as_deref())
|
|
.await
|
|
.map_err(ApiError)?;
|
|
|
|
Ok(Json(SavedSearchResponse {
|
|
id: id.to_string(),
|
|
name: req.name,
|
|
query: req.query,
|
|
sort_order: req.sort_order,
|
|
created_at: chrono::Utc::now(),
|
|
}))
|
|
}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/api/v1/searches",
|
|
tag = "saved_searches",
|
|
responses(
|
|
(status = 200, description = "List of saved searches", body = Vec<SavedSearchResponse>),
|
|
(status = 401, description = "Unauthorized"),
|
|
(status = 500, description = "Internal server error"),
|
|
),
|
|
security(("bearer_auth" = []))
|
|
)]
|
|
pub async fn list_saved_searches(
|
|
State(state): State<AppState>,
|
|
) -> Result<Json<Vec<SavedSearchResponse>>, ApiError> {
|
|
let searches = state
|
|
.storage
|
|
.list_saved_searches()
|
|
.await
|
|
.map_err(ApiError)?;
|
|
Ok(Json(
|
|
searches
|
|
.into_iter()
|
|
.map(|s| {
|
|
SavedSearchResponse {
|
|
id: s.id.to_string(),
|
|
name: s.name,
|
|
query: s.query,
|
|
sort_order: s.sort_order,
|
|
created_at: s.created_at,
|
|
}
|
|
})
|
|
.collect(),
|
|
))
|
|
}
|
|
|
|
#[utoipa::path(
|
|
delete,
|
|
path = "/api/v1/searches/{id}",
|
|
tag = "saved_searches",
|
|
params(("id" = uuid::Uuid, Path, description = "Saved search ID")),
|
|
responses(
|
|
(status = 200, description = "Saved search deleted"),
|
|
(status = 401, description = "Unauthorized"),
|
|
(status = 404, description = "Not found"),
|
|
(status = 500, description = "Internal server error"),
|
|
),
|
|
security(("bearer_auth" = []))
|
|
)]
|
|
pub async fn delete_saved_search(
|
|
State(state): State<AppState>,
|
|
Path(id): Path<uuid::Uuid>,
|
|
) -> Result<Json<serde_json::Value>, ApiError> {
|
|
state
|
|
.storage
|
|
.delete_saved_search(id)
|
|
.await
|
|
.map_err(ApiError)?;
|
|
Ok(Json(serde_json::json!({ "deleted": true })))
|
|
}
|