902 lines
28 KiB
Rust
902 lines
28 KiB
Rust
use std::future::Future;
|
|
use std::sync::Arc;
|
|
|
|
use axum::extract::{Multipart, State};
|
|
use axum::Json;
|
|
use serde::Deserialize;
|
|
|
|
use crate::error::AppError;
|
|
use crate::state::WebState;
|
|
|
|
async fn run_cancellable<T, F>(state: &Arc<WebState>, execution_id: Option<String>, future: F) -> Result<T, AppError>
|
|
where
|
|
F: Future<Output = Result<T, String>>,
|
|
{
|
|
let registered = execution_id
|
|
.as_ref()
|
|
.filter(|id| !id.trim().is_empty())
|
|
.map(|id| state.app.running_queries.register(id.clone()));
|
|
if let Some(query) = registered.as_ref() {
|
|
let token = query.token();
|
|
tokio::select! {
|
|
biased;
|
|
_ = token.cancelled() => Err(AppError::from(dbx_core::query::canceled_error())),
|
|
result = future => result.map_err(AppError::from),
|
|
}
|
|
} else {
|
|
future.await.map_err(AppError::from)
|
|
}
|
|
}
|
|
|
|
async fn ensure_writable(
|
|
app: &dbx_core::connection::AppState,
|
|
connection_id: &str,
|
|
action: &str,
|
|
) -> Result<(), AppError> {
|
|
if let Some(name) = dbx_core::query::connection_readonly_name(app, connection_id).await {
|
|
return Err(AppError::from(format!(
|
|
"Read-only mode: connection '{}' has read-only protection enabled. {} blocked.",
|
|
name, action
|
|
)));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct DocumentListDatabasesRequest {
|
|
pub connection_id: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct DocumentListCollectionsRequest {
|
|
pub connection_id: String,
|
|
pub database: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct DocumentFindRequest {
|
|
pub connection_id: String,
|
|
pub database: String,
|
|
pub collection: String,
|
|
pub skip: Option<u64>,
|
|
pub limit: Option<i64>,
|
|
pub filter: Option<String>,
|
|
pub projection: Option<String>,
|
|
pub sort: Option<String>,
|
|
pub collation: Option<String>,
|
|
pub cursor: Option<String>,
|
|
#[serde(default)]
|
|
pub cursor_pagination: bool,
|
|
pub execution_id: Option<String>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct DocumentCountRequest {
|
|
pub connection_id: String,
|
|
pub collection: String,
|
|
pub filter: Option<String>,
|
|
pub execution_id: Option<String>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct DynamoDbDescribeTableRequest {
|
|
pub connection_id: String,
|
|
pub table: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ElasticsearchCountDocumentsRequest {
|
|
pub connection_id: String,
|
|
pub index: String,
|
|
pub filter: Option<String>,
|
|
pub execution_id: Option<String>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ElasticsearchIndexRequest {
|
|
pub connection_id: String,
|
|
pub index: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ElasticsearchIndexMetadataRequest {
|
|
pub connection_id: String,
|
|
pub index: String,
|
|
pub kind: dbx_core::document_ops::ElasticsearchIndexMetadataKind,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct DocumentInsertRequest {
|
|
pub connection_id: String,
|
|
pub database: String,
|
|
pub collection: String,
|
|
pub doc_json: String,
|
|
pub routing: Option<String>,
|
|
pub preserve_bson_types: Option<bool>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct DocumentUpdateRequest {
|
|
pub connection_id: String,
|
|
pub database: String,
|
|
pub collection: String,
|
|
pub id: String,
|
|
pub doc_json: String,
|
|
pub routing: Option<String>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct DocumentDeleteRequest {
|
|
pub connection_id: String,
|
|
pub database: String,
|
|
pub collection: String,
|
|
pub id: String,
|
|
pub routing: Option<String>,
|
|
pub document_type: Option<String>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct MeilisearchBatchSaveRequest {
|
|
pub connection_id: String,
|
|
pub collection: String,
|
|
pub updates: Vec<dbx_core::db::meilisearch_driver::MeilisearchDocumentUpdate>,
|
|
pub delete_ids: Vec<String>,
|
|
pub inserts: Vec<String>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct MeilisearchSearchRequest {
|
|
pub connection_id: String,
|
|
pub index: String,
|
|
pub q: Option<String>,
|
|
pub filter: Option<String>,
|
|
pub sort: Option<String>,
|
|
pub limit: u64,
|
|
pub offset: u64,
|
|
pub hybrid_embedder: Option<String>,
|
|
pub hybrid_semantic_ratio: Option<f64>,
|
|
pub show_ranking_score: Option<bool>,
|
|
pub ranking_score_threshold: Option<f64>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct MeilisearchDocumentPageRequest {
|
|
pub connection_id: String,
|
|
pub index: String,
|
|
pub filter: Option<String>,
|
|
pub sort: Option<String>,
|
|
pub limit: u64,
|
|
pub offset: u64,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct MeilisearchIndexRequest {
|
|
pub connection_id: String,
|
|
pub index: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct MeilisearchDocumentGetRequest {
|
|
pub connection_id: String,
|
|
pub index: String,
|
|
pub id: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct MeilisearchSettingsUpdateRequest {
|
|
pub connection_id: String,
|
|
pub index: String,
|
|
pub settings: serde_json::Value,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct MeilisearchConnectionRequest {
|
|
pub connection_id: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct MeilisearchKeyListRequest {
|
|
pub connection_id: String,
|
|
pub offset: Option<u64>,
|
|
pub limit: Option<u64>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct MeilisearchKeyRequest {
|
|
pub connection_id: String,
|
|
pub uid: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct MeilisearchKeyCreateRequest {
|
|
pub connection_id: String,
|
|
pub input: dbx_core::db::meilisearch_driver::MeilisearchKeyCreateInput,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct MeilisearchKeyUpdateRequest {
|
|
pub connection_id: String,
|
|
pub uid: String,
|
|
pub input: dbx_core::db::meilisearch_driver::MeilisearchKeyUpdateInput,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct MeilisearchTaskListRequest {
|
|
pub connection_id: String,
|
|
pub input: dbx_core::db::meilisearch_driver::MeilisearchTaskListInput,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct MeilisearchTaskRequest {
|
|
pub connection_id: String,
|
|
pub uid: u64,
|
|
pub expected_index_uid: Option<String>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct MeilisearchTaskMutationRequest {
|
|
pub connection_id: String,
|
|
pub selector: dbx_core::db::meilisearch_driver::MeilisearchTaskSelector,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct GridFsBucketRequest {
|
|
pub connection_id: String,
|
|
pub database: String,
|
|
pub bucket: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct GridFsFileListRequest {
|
|
pub connection_id: String,
|
|
pub database: String,
|
|
pub bucket: String,
|
|
pub filter: Option<String>,
|
|
pub sort: Option<String>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct GridFsBucketListRequest {
|
|
pub connection_id: String,
|
|
pub database: String,
|
|
pub filter: Option<String>,
|
|
pub sort: Option<String>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct GridFsDownloadRequest {
|
|
pub connection_id: String,
|
|
pub database: String,
|
|
pub bucket: String,
|
|
pub file_id: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct GridFsFileDeleteRequest {
|
|
pub connection_id: String,
|
|
pub database: String,
|
|
pub bucket: String,
|
|
pub file_id: String,
|
|
}
|
|
|
|
pub async fn list_databases(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<DocumentListDatabasesRequest>,
|
|
) -> Result<Json<Vec<String>>, AppError> {
|
|
let result =
|
|
dbx_core::document_ops::list_databases_core(&state.app, &req.connection_id).await.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn list_collections(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<DocumentListCollectionsRequest>,
|
|
) -> Result<Json<Vec<dbx_core::document_ops::CollectionInfo>>, AppError> {
|
|
let result = dbx_core::document_ops::list_collections_core(&state.app, &req.connection_id, &req.database)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn find_documents(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<DocumentFindRequest>,
|
|
) -> Result<Json<dbx_core::db::document_result::DocumentQueryResult>, AppError> {
|
|
let result = run_cancellable(
|
|
&state,
|
|
req.execution_id,
|
|
dbx_core::document_ops::find_documents_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.database,
|
|
&req.collection,
|
|
req.skip.unwrap_or(0),
|
|
req.limit.unwrap_or(50),
|
|
req.filter.as_deref(),
|
|
req.projection.as_deref(),
|
|
req.sort.as_deref(),
|
|
req.collation.as_deref(),
|
|
req.cursor.as_deref(),
|
|
req.cursor_pagination,
|
|
),
|
|
)
|
|
.await?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn count_documents(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<DocumentCountRequest>,
|
|
) -> Result<Json<u64>, AppError> {
|
|
let result = run_cancellable(
|
|
&state,
|
|
req.execution_id,
|
|
dbx_core::document_ops::count_document_store_documents_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.collection,
|
|
req.filter.as_deref(),
|
|
),
|
|
)
|
|
.await?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn describe_dynamodb_table(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<DynamoDbDescribeTableRequest>,
|
|
) -> Result<Json<dbx_core::db::dynamodb_driver::DynamoDbTableDescription>, AppError> {
|
|
let result = dbx_core::document_ops::describe_dynamodb_table_core(&state.app, &req.connection_id, &req.table)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn elasticsearch_count_documents(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<ElasticsearchCountDocumentsRequest>,
|
|
) -> Result<Json<u64>, AppError> {
|
|
let result = run_cancellable(
|
|
&state,
|
|
req.execution_id,
|
|
dbx_core::document_ops::count_elasticsearch_documents_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.index,
|
|
req.filter.as_deref(),
|
|
),
|
|
)
|
|
.await?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn elasticsearch_get_index_metadata(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<ElasticsearchIndexMetadataRequest>,
|
|
) -> Result<Json<serde_json::Value>, AppError> {
|
|
let result = dbx_core::document_ops::elasticsearch_get_index_metadata_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.index,
|
|
req.kind,
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn elasticsearch_delete_all_documents(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<ElasticsearchIndexRequest>,
|
|
) -> Result<Json<dbx_core::db::elasticsearch_driver::ElasticsearchDeleteByQueryResult>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Delete all documents").await?;
|
|
let result =
|
|
dbx_core::document_ops::elasticsearch_delete_all_documents_core(&state.app, &req.connection_id, &req.index)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn insert_document(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<DocumentInsertRequest>,
|
|
) -> Result<Json<String>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Insert").await?;
|
|
let result = if req.preserve_bson_types.unwrap_or(false) {
|
|
dbx_core::document_ops::insert_document_preserving_bson_types_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.database,
|
|
&req.collection,
|
|
&req.doc_json,
|
|
req.routing.as_deref(),
|
|
)
|
|
.await
|
|
} else {
|
|
dbx_core::document_ops::insert_document_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.database,
|
|
&req.collection,
|
|
&req.doc_json,
|
|
req.routing.as_deref(),
|
|
)
|
|
.await
|
|
}
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn update_document(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<DocumentUpdateRequest>,
|
|
) -> Result<Json<u64>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Update").await?;
|
|
let result = dbx_core::document_ops::update_document_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.database,
|
|
&req.collection,
|
|
&req.id,
|
|
&req.doc_json,
|
|
req.routing.as_deref(),
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn delete_document(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<DocumentDeleteRequest>,
|
|
) -> Result<Json<u64>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Delete").await?;
|
|
let result = dbx_core::document_ops::delete_document_core_with_type(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.database,
|
|
&req.collection,
|
|
&req.id,
|
|
req.routing.as_deref(),
|
|
req.document_type.as_deref(),
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn save_meilisearch_batch(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchBatchSaveRequest>,
|
|
) -> Result<Json<u64>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Save").await?;
|
|
let result = dbx_core::document_ops::save_meilisearch_document_batch_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.collection,
|
|
&req.updates,
|
|
&req.delete_ids,
|
|
&req.inserts,
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_search(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchSearchRequest>,
|
|
) -> Result<Json<dbx_core::db::meilisearch_driver::MeilisearchSearchResult>, AppError> {
|
|
let result = dbx_core::document_ops::meilisearch_search_documents_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.index,
|
|
req.q.as_deref(),
|
|
req.filter.as_deref(),
|
|
req.sort.as_deref(),
|
|
req.limit,
|
|
req.offset,
|
|
req.hybrid_embedder.as_deref(),
|
|
req.hybrid_semantic_ratio,
|
|
req.show_ranking_score.unwrap_or(false),
|
|
req.ranking_score_threshold,
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_fetch_documents(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchDocumentPageRequest>,
|
|
) -> Result<Json<dbx_core::db::meilisearch_driver::MeilisearchDocumentPage>, AppError> {
|
|
let result = dbx_core::document_ops::meilisearch_fetch_document_page_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.index,
|
|
req.filter.as_deref(),
|
|
req.sort.as_deref(),
|
|
req.limit,
|
|
req.offset,
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_get_document(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchDocumentGetRequest>,
|
|
) -> Result<Json<String>, AppError> {
|
|
let result =
|
|
dbx_core::document_ops::meilisearch_get_document_core(&state.app, &req.connection_id, &req.index, &req.id)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_get_settings(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchIndexRequest>,
|
|
) -> Result<Json<serde_json::Value>, AppError> {
|
|
let result =
|
|
dbx_core::document_ops::meilisearch_get_index_settings_core(&state.app, &req.connection_id, &req.index)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_update_settings(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchSettingsUpdateRequest>,
|
|
) -> Result<Json<()>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Update settings").await?;
|
|
dbx_core::document_ops::meilisearch_update_index_settings_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.index,
|
|
&req.settings,
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(()))
|
|
}
|
|
|
|
pub async fn meilisearch_get_stats(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchIndexRequest>,
|
|
) -> Result<Json<serde_json::Value>, AppError> {
|
|
let result = dbx_core::document_ops::meilisearch_get_index_stats_core(&state.app, &req.connection_id, &req.index)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_get_overview(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchIndexRequest>,
|
|
) -> Result<Json<dbx_core::db::meilisearch_driver::MeilisearchIndexOverview>, AppError> {
|
|
let result =
|
|
dbx_core::document_ops::meilisearch_get_index_overview_core(&state.app, &req.connection_id, &req.index)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_delete_index(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchIndexRequest>,
|
|
) -> Result<Json<()>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Delete index").await?;
|
|
dbx_core::document_ops::meilisearch_delete_index_core(&state.app, &req.connection_id, &req.index)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(()))
|
|
}
|
|
|
|
pub async fn meilisearch_delete_all_documents(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchIndexRequest>,
|
|
) -> Result<Json<()>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Delete all documents").await?;
|
|
dbx_core::document_ops::meilisearch_delete_all_documents_core(&state.app, &req.connection_id, &req.index)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(()))
|
|
}
|
|
|
|
pub async fn meilisearch_get_system_overview(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchConnectionRequest>,
|
|
) -> Result<Json<dbx_core::db::meilisearch_driver::MeilisearchSystemOverview>, AppError> {
|
|
let result = dbx_core::document_ops::meilisearch_get_system_overview_core(&state.app, &req.connection_id)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_list_keys(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchKeyListRequest>,
|
|
) -> Result<Json<dbx_core::db::meilisearch_driver::MeilisearchKeyPage>, AppError> {
|
|
let result = dbx_core::document_ops::meilisearch_list_keys_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
req.offset.unwrap_or(0),
|
|
req.limit.unwrap_or(20),
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_get_key(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchKeyRequest>,
|
|
) -> Result<Json<dbx_core::db::meilisearch_driver::MeilisearchKeyListItem>, AppError> {
|
|
let result = dbx_core::document_ops::meilisearch_get_key_core(&state.app, &req.connection_id, &req.uid)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_create_key(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchKeyCreateRequest>,
|
|
) -> Result<Json<dbx_core::db::meilisearch_driver::MeilisearchCreatedKey>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Create API key").await?;
|
|
let result = dbx_core::document_ops::meilisearch_create_key_core(&state.app, &req.connection_id, &req.input)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_update_key(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchKeyUpdateRequest>,
|
|
) -> Result<Json<dbx_core::db::meilisearch_driver::MeilisearchKeyListItem>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Update API key").await?;
|
|
let result =
|
|
dbx_core::document_ops::meilisearch_update_key_core(&state.app, &req.connection_id, &req.uid, &req.input)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_delete_key(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchKeyRequest>,
|
|
) -> Result<Json<()>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Delete API key").await?;
|
|
dbx_core::document_ops::meilisearch_delete_key_core(&state.app, &req.connection_id, &req.uid)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(()))
|
|
}
|
|
|
|
pub async fn meilisearch_get_tasks(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchTaskListRequest>,
|
|
) -> Result<Json<dbx_core::db::meilisearch_driver::MeilisearchTaskPage>, AppError> {
|
|
let result = dbx_core::document_ops::meilisearch_get_tasks_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.input.selector,
|
|
req.input.from,
|
|
req.input.limit.unwrap_or(20),
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_get_task(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchTaskRequest>,
|
|
) -> Result<Json<dbx_core::db::meilisearch_driver::MeilisearchTask>, AppError> {
|
|
let result = dbx_core::document_ops::meilisearch_get_task_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
req.uid,
|
|
req.expected_index_uid.as_deref(),
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_cancel_tasks(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchTaskMutationRequest>,
|
|
) -> Result<Json<dbx_core::db::meilisearch_driver::MeilisearchEnqueuedTaskSummary>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Cancel tasks").await?;
|
|
let result = dbx_core::document_ops::meilisearch_cancel_tasks_core(&state.app, &req.connection_id, &req.selector)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn meilisearch_delete_tasks(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<MeilisearchTaskMutationRequest>,
|
|
) -> Result<Json<dbx_core::db::meilisearch_driver::MeilisearchEnqueuedTaskSummary>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Delete tasks").await?;
|
|
let result = dbx_core::document_ops::meilisearch_delete_tasks_core(&state.app, &req.connection_id, &req.selector)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn list_gridfs_files(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<GridFsFileListRequest>,
|
|
) -> Result<Json<Vec<dbx_core::document_ops::MongoGridFsFileInfo>>, AppError> {
|
|
let result = dbx_core::document_ops::list_gridfs_files_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.database,
|
|
&req.bucket,
|
|
req.filter.as_deref(),
|
|
req.sort.as_deref(),
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn list_gridfs_buckets(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<GridFsBucketListRequest>,
|
|
) -> Result<Json<Vec<dbx_core::document_ops::MongoGridFsBucketInfo>>, AppError> {
|
|
let result = dbx_core::document_ops::list_gridfs_buckets_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.database,
|
|
req.filter.as_deref(),
|
|
req.sort.as_deref(),
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn create_gridfs_bucket(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<GridFsBucketRequest>,
|
|
) -> Result<Json<()>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Create GridFS bucket").await?;
|
|
dbx_core::document_ops::create_gridfs_bucket_core(&state.app, &req.connection_id, &req.database, &req.bucket)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(()))
|
|
}
|
|
|
|
pub async fn delete_gridfs_bucket(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<GridFsBucketRequest>,
|
|
) -> Result<Json<()>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Delete GridFS bucket").await?;
|
|
dbx_core::document_ops::delete_gridfs_bucket_core(&state.app, &req.connection_id, &req.database, &req.bucket)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(()))
|
|
}
|
|
|
|
pub async fn download_gridfs_file(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<GridFsDownloadRequest>,
|
|
) -> Result<Json<Vec<u8>>, AppError> {
|
|
let result = dbx_core::document_ops::download_gridfs_file_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.database,
|
|
&req.bucket,
|
|
&req.file_id,
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn upload_gridfs_file(
|
|
State(state): State<Arc<WebState>>,
|
|
mut multipart: Multipart,
|
|
) -> Result<Json<String>, AppError> {
|
|
let mut connection_id: Option<String> = None;
|
|
let mut database: Option<String> = None;
|
|
let mut bucket: Option<String> = None;
|
|
let mut file_name: Option<String> = None;
|
|
let mut content_type: Option<String> = None;
|
|
let mut file_bytes: Option<Vec<u8>> = None;
|
|
|
|
while let Some(field) = multipart.next_field().await.map_err(|e| AppError::from(e.to_string()))? {
|
|
let name = field.name().unwrap_or_default().to_string();
|
|
match name.as_str() {
|
|
"connectionId" => connection_id = Some(field.text().await.map_err(|e| AppError::from(e.to_string()))?),
|
|
"database" => database = Some(field.text().await.map_err(|e| AppError::from(e.to_string()))?),
|
|
"bucket" => bucket = Some(field.text().await.map_err(|e| AppError::from(e.to_string()))?),
|
|
"fileName" => file_name = Some(field.text().await.map_err(|e| AppError::from(e.to_string()))?),
|
|
"contentType" => content_type = Some(field.text().await.map_err(|e| AppError::from(e.to_string()))?),
|
|
"file" => {
|
|
if file_name.is_none() {
|
|
file_name = field.file_name().map(str::to_string);
|
|
}
|
|
if content_type.is_none() {
|
|
content_type = field.content_type().map(str::to_string);
|
|
}
|
|
file_bytes = Some(field.bytes().await.map_err(|e| AppError::from(e.to_string()))?.to_vec());
|
|
}
|
|
_ => {
|
|
let _ = field.bytes().await;
|
|
}
|
|
}
|
|
}
|
|
|
|
let connection_id = connection_id.ok_or_else(|| AppError::from("Missing connectionId".to_string()))?;
|
|
let database = database.ok_or_else(|| AppError::from("Missing database".to_string()))?;
|
|
let bucket = bucket.ok_or_else(|| AppError::from("Missing bucket".to_string()))?;
|
|
let file_name = file_name.ok_or_else(|| AppError::from("Missing fileName".to_string()))?;
|
|
let file_bytes = file_bytes.ok_or_else(|| AppError::from("No file uploaded".to_string()))?;
|
|
|
|
ensure_writable(&state.app, &connection_id, "Upload GridFS file").await?;
|
|
let result = dbx_core::document_ops::upload_gridfs_file_core(
|
|
&state.app,
|
|
&connection_id,
|
|
&database,
|
|
&bucket,
|
|
&file_name,
|
|
&file_bytes,
|
|
content_type.as_deref(),
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(result))
|
|
}
|
|
|
|
pub async fn delete_gridfs_file(
|
|
State(state): State<Arc<WebState>>,
|
|
Json(req): Json<GridFsFileDeleteRequest>,
|
|
) -> Result<Json<()>, AppError> {
|
|
ensure_writable(&state.app, &req.connection_id, "Delete GridFS file").await?;
|
|
dbx_core::document_ops::delete_gridfs_file_core(
|
|
&state.app,
|
|
&req.connection_id,
|
|
&req.database,
|
|
&req.bucket,
|
|
&req.file_id,
|
|
)
|
|
.await
|
|
.map_err(AppError::from)?;
|
|
Ok(Json(()))
|
|
}
|